2018-12-28 16:01:58 +00:00
|
|
|
// 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.
|
|
|
|
|
2020-01-02 11:49:27 +00:00
|
|
|
use std::ffi::OsStr;
|
2018-12-28 16:01:58 +00:00
|
|
|
use std::io::{self, Read, Write};
|
2020-01-02 11:49:27 +00:00
|
|
|
use std::iter::once;
|
|
|
|
use std::os::windows::ffi::OsStrExt;
|
2019-03-07 20:37:11 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2019-11-16 21:11:56 +00:00
|
|
|
use std::sync::mpsc::TryRecvError;
|
2018-12-28 16:01:58 +00:00
|
|
|
|
|
|
|
use mio::{self, Evented, Poll, PollOpt, Ready, Token};
|
|
|
|
use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite};
|
|
|
|
use mio_named_pipes::NamedPipe;
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
use log::info;
|
2018-12-28 16:01:58 +00:00
|
|
|
|
2020-01-02 11:49:27 +00:00
|
|
|
use crate::config::{Config, Shell};
|
2019-10-05 00:29:26 +00:00
|
|
|
use crate::event::OnResize;
|
2018-12-28 16:01:58 +00:00
|
|
|
use crate::term::SizeInfo;
|
2019-11-16 21:11:56 +00:00
|
|
|
use crate::tty::windows::child::ChildExitWatcher;
|
|
|
|
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
|
2018-12-28 16:01:58 +00:00
|
|
|
|
2019-11-16 21:11:56 +00:00
|
|
|
mod child;
|
2018-12-28 16:01:58 +00:00
|
|
|
mod conpty;
|
|
|
|
mod winpty;
|
|
|
|
|
2019-03-07 20:37:11 +00:00
|
|
|
static IS_CONPTY: AtomicBool = AtomicBool::new(false);
|
2018-12-28 16:01:58 +00:00
|
|
|
|
2019-03-07 20:37:11 +00:00
|
|
|
pub fn is_conpty() -> bool {
|
|
|
|
IS_CONPTY.load(Ordering::Relaxed)
|
|
|
|
}
|
|
|
|
|
2019-12-14 21:32:24 +00:00
|
|
|
enum PtyBackend {
|
|
|
|
Winpty(winpty::Agent),
|
|
|
|
Conpty(conpty::Conpty),
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
|
|
|
|
2019-12-05 17:14:47 +00:00
|
|
|
pub struct Pty {
|
2019-12-14 21:32:24 +00:00
|
|
|
// XXX: Backend is required to be the first field, to ensure correct drop order. Dropping
|
|
|
|
// `conout` before `backend` will cause a deadlock.
|
|
|
|
backend: PtyBackend,
|
2018-12-28 16:01:58 +00:00
|
|
|
// TODO: It's on the roadmap for the Conpty API to support Overlapped I/O.
|
2020-05-05 22:50:23 +00:00
|
|
|
// See https://github.com/Microsoft/console/issues/262.
|
2018-12-28 16:01:58 +00:00
|
|
|
// When support for that lands then it should be possible to use
|
2020-05-05 22:50:23 +00:00
|
|
|
// NamedPipe for the conout and conin handles.
|
2018-12-28 16:01:58 +00:00
|
|
|
conout: EventedReadablePipe,
|
|
|
|
conin: EventedWritablePipe,
|
|
|
|
read_token: mio::Token,
|
|
|
|
write_token: mio::Token,
|
2019-11-16 21:11:56 +00:00
|
|
|
child_event_token: mio::Token,
|
|
|
|
child_watcher: ChildExitWatcher,
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
|
|
|
|
2019-12-05 17:14:47 +00:00
|
|
|
pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) -> Pty {
|
2019-05-10 11:36:16 +00:00
|
|
|
if let Some(pty) = conpty::new(config, size, window_id) {
|
2019-12-21 21:23:18 +00:00
|
|
|
info!("Using ConPTY backend");
|
2019-03-07 20:37:11 +00:00
|
|
|
IS_CONPTY.store(true, Ordering::Relaxed);
|
2018-12-28 16:01:58 +00:00
|
|
|
pty
|
|
|
|
} else {
|
2019-12-21 21:23:18 +00:00
|
|
|
info!("Using WinPTY backend");
|
2019-05-10 11:36:16 +00:00
|
|
|
winpty::new(config, size, window_id)
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-28 21:42:43 +00:00
|
|
|
// TODO: The ConPTY API currently must use synchronous pipes as the input
|
2018-12-28 16:01:58 +00:00
|
|
|
// 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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 17:14:47 +00:00
|
|
|
impl EventedReadWrite for Pty {
|
2018-12-28 16:01:58 +00:00
|
|
|
type Reader = EventedReadablePipe;
|
|
|
|
type Writer = EventedWritablePipe;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn register(
|
|
|
|
&mut self,
|
|
|
|
poll: &mio::Poll,
|
2019-03-12 19:44:47 +00:00
|
|
|
token: &mut dyn Iterator<Item = mio::Token>,
|
2018-12-28 16:01:58 +00:00
|
|
|
interest: mio::Ready,
|
|
|
|
poll_opts: mio::PollOpt,
|
|
|
|
) -> io::Result<()> {
|
2019-03-12 19:44:47 +00:00
|
|
|
self.read_token = token.next().unwrap();
|
|
|
|
self.write_token = token.next().unwrap();
|
2018-12-28 16:01:58 +00:00
|
|
|
|
|
|
|
if interest.is_readable() {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.register(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?
|
2018-12-28 16:01:58 +00:00
|
|
|
} else {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.register(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
|
|
|
if interest.is_writable() {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.register(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?
|
2018-12-28 16:01:58 +00:00
|
|
|
} else {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.register(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
2019-11-16 21:11:56 +00:00
|
|
|
|
|
|
|
self.child_event_token = token.next().unwrap();
|
|
|
|
poll.register(
|
|
|
|
self.child_watcher.event_rx(),
|
|
|
|
self.child_event_token,
|
|
|
|
mio::Ready::readable(),
|
|
|
|
poll_opts,
|
|
|
|
)?;
|
|
|
|
|
2018-12-28 16:01:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn reregister(
|
|
|
|
&mut self,
|
|
|
|
poll: &mio::Poll,
|
|
|
|
interest: mio::Ready,
|
|
|
|
poll_opts: mio::PollOpt,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
if interest.is_readable() {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.reregister(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?;
|
2018-12-28 16:01:58 +00:00
|
|
|
} else {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.reregister(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?;
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
|
|
|
if interest.is_writable() {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.reregister(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?;
|
2018-12-28 16:01:58 +00:00
|
|
|
} else {
|
2019-03-30 16:48:36 +00:00
|
|
|
poll.reregister(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?;
|
2018-12-28 16:01:58 +00:00
|
|
|
}
|
2019-11-16 21:11:56 +00:00
|
|
|
|
|
|
|
poll.reregister(
|
|
|
|
self.child_watcher.event_rx(),
|
|
|
|
self.child_event_token,
|
|
|
|
mio::Ready::readable(),
|
|
|
|
poll_opts,
|
|
|
|
)?;
|
|
|
|
|
2018-12-28 16:01:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> {
|
|
|
|
poll.deregister(&self.conout)?;
|
|
|
|
poll.deregister(&self.conin)?;
|
2019-11-16 21:11:56 +00:00
|
|
|
poll.deregister(self.child_watcher.event_rx())?;
|
2018-12-28 16:01:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn reader(&mut self) -> &mut Self::Reader {
|
|
|
|
&mut self.conout
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn read_token(&self) -> mio::Token {
|
|
|
|
self.read_token
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn writer(&mut self) -> &mut Self::Writer {
|
|
|
|
&mut self.conin
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn write_token(&self) -> mio::Token {
|
|
|
|
self.write_token
|
|
|
|
}
|
|
|
|
}
|
2019-03-12 19:44:47 +00:00
|
|
|
|
2019-12-05 17:14:47 +00:00
|
|
|
impl EventedPty for Pty {
|
2019-11-16 21:11:56 +00:00
|
|
|
fn child_event_token(&self) -> mio::Token {
|
|
|
|
self.child_event_token
|
|
|
|
}
|
|
|
|
|
|
|
|
fn next_child_event(&mut self) -> Option<ChildEvent> {
|
|
|
|
match self.child_watcher.event_rx().try_recv() {
|
|
|
|
Ok(ev) => Some(ev),
|
|
|
|
Err(TryRecvError::Empty) => None,
|
|
|
|
Err(TryRecvError::Disconnected) => Some(ChildEvent::Exited),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-14 21:32:24 +00:00
|
|
|
|
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-02 11:49:27 +00:00
|
|
|
|
|
|
|
fn cmdline<C>(config: &Config<C>) -> String {
|
|
|
|
let default_shell = Shell::new("powershell");
|
|
|
|
let shell = config.shell.as_ref().unwrap_or(&default_shell);
|
|
|
|
|
|
|
|
once(shell.program.as_ref())
|
|
|
|
.chain(shell.args.iter().map(|a| a.as_ref()))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(" ")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Converts the string slice into a Windows-standard representation for "W"-
|
|
|
|
/// suffixed function variants, which accept UTF-16 encoded string values.
|
|
|
|
pub fn win32_string<S: AsRef<OsStr> + ?Sized>(value: &S) -> Vec<u16> {
|
|
|
|
OsStr::new(value).encode_wide().chain(once(0)).collect()
|
|
|
|
}
|