alacritty/alacritty_terminal/src/event_loop.rs

414 lines
13 KiB
Rust
Raw Normal View History

2020-05-05 22:50:23 +00:00
//! The main event loop which performs I/O on the pseudoterminal.
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fs::File;
2019-03-30 16:48:36 +00:00
use std::io::{self, ErrorKind, Read, Write};
use std::marker::Send;
2019-03-30 16:48:36 +00:00
use std::sync::Arc;
use std::thread::JoinHandle;
use log::error;
#[cfg(not(windows))]
2017-08-30 18:43:37 +00:00
use mio::unix::UnixReady;
use mio::{self, Events, PollOpt, Ready};
use mio_extras::channel::{self, Receiver, Sender};
use crate::ansi;
use crate::event::{self, Event, EventListener};
2019-03-30 16:48:36 +00:00
use crate::sync::FairMutex;
use crate::term::{SizeInfo, Term};
use crate::thread;
2019-03-30 16:48:36 +00:00
use crate::tty;
2020-05-05 22:50:23 +00:00
/// Max bytes to read from the PTY.
Replace serde's derive with custom proc macro This replaces the existing `Deserialize` derive from serde with a `ConfigDeserialize` derive. The goal of this new proc macro is to allow a more error-friendly deserialization for the Alacritty configuration file without having to manage a lot of boilerplate code inside the configuration modules. The first part of the derive macro is for struct deserialization. This takes structs which have `Default` implemented and will only replace fields which can be successfully deserialized. Otherwise the `log` crate is used for printing errors. Since this deserialization takes the default value from the struct instead of the value, it removes the necessity for creating new types just to implement `Default` on them for deserialization. Additionally, the struct deserialization also checks for `Option` values and makes sure that explicitly specifying `none` as text literal is allowed for all options. The other part of the derive macro is responsible for deserializing enums. While only enums with Unit variants are supported, it will automatically implement a deserializer for these enums which accepts any form of capitalization. Since this custom derive prevents us from using serde's attributes on fields, some of the attributes have been reimplemented for `ConfigDeserialize`. These include `#[config(flatten)]`, `#[config(skip)]` and `#[config(alias = "alias)]`. The flatten attribute is currently limited to at most one per struct. Additionally the `#[config(deprecated = "optional message")]` attribute allows easily defining uniform deprecation messages for fields on structs.
2020-12-21 02:44:38 +00:00
const MAX_READ: usize = u16::max_value() as usize;
2020-05-05 22:50:23 +00:00
/// Messages that may be sent to the `EventLoop`.
#[derive(Debug)]
pub enum Msg {
2020-05-05 22:50:23 +00:00
/// Data that should be written to the PTY.
Input(Cow<'static, [u8]>),
2020-05-05 22:50:23 +00:00
/// Indicates that the `EventLoop` should shut down, as Alacritty is shutting down.
Shutdown,
2020-05-05 22:50:23 +00:00
/// Instruction to resize the PTY.
Resize(SizeInfo),
}
/// The main event!.. loop.
///
2020-05-05 22:50:23 +00:00
/// Handles all the PTY I/O and runs the PTY parser which updates terminal
/// state.
pub struct EventLoop<T: tty::EventedPty, U: EventListener> {
poll: mio::Poll,
pty: T,
2017-08-30 18:43:37 +00:00
rx: Receiver<Msg>,
tx: Sender<Msg>,
terminal: Arc<FairMutex<Term<U>>>,
event_proxy: U,
hold: bool,
ref_test: bool,
}
/// Helper type which tracks how much of a buffer has been written.
struct Writing {
source: Cow<'static, [u8]>,
written: usize,
}
2020-05-05 22:50:23 +00:00
/// All of the mutable state needed to run the event loop.
///
/// Contains list of items to write, current write state, etc. Anything that
/// would otherwise be mutated on the `EventLoop` goes here.
pub struct State {
write_list: VecDeque<Cow<'static, [u8]>>,
writing: Option<Writing>,
parser: ansi::Processor,
}
2017-08-30 18:43:37 +00:00
pub struct Notifier(pub Sender<Msg>);
impl event::Notify for Notifier {
fn notify<B>(&mut self, bytes: B)
2019-03-30 16:48:36 +00:00
where
B: Into<Cow<'static, [u8]>>,
{
let bytes = bytes.into();
// terminal hangs if we send 0 bytes through.
if bytes.len() == 0 {
2019-03-30 16:48:36 +00:00
return;
}
self.0.send(Msg::Input(bytes)).expect("send event loop msg");
}
}
impl event::OnResize for Notifier {
fn on_resize(&mut self, size: &SizeInfo) {
self.0.send(Msg::Resize(*size)).expect("expected send event loop msg");
}
}
impl Default for State {
fn default() -> State {
2019-03-30 16:48:36 +00:00
State { write_list: VecDeque::new(), parser: ansi::Processor::new(), writing: None }
}
}
impl State {
#[inline]
fn ensure_next(&mut self) {
if self.writing.is_none() {
self.goto_next();
}
}
#[inline]
fn goto_next(&mut self) {
2019-03-30 16:48:36 +00:00
self.writing = self.write_list.pop_front().map(Writing::new);
}
#[inline]
fn take_current(&mut self) -> Option<Writing> {
self.writing.take()
}
#[inline]
fn needs_write(&self) -> bool {
self.writing.is_some() || !self.write_list.is_empty()
}
#[inline]
fn set_current(&mut self, new: Option<Writing>) {
self.writing = new;
}
}
impl Writing {
#[inline]
fn new(c: Cow<'static, [u8]>) -> Writing {
Writing { source: c, written: 0 }
}
#[inline]
fn advance(&mut self, n: usize) {
self.written += n;
}
#[inline]
fn remaining_bytes(&self) -> &[u8] {
&self.source[self.written..]
}
#[inline]
fn finished(&self) -> bool {
self.written >= self.source.len()
}
}
impl<T, U> EventLoop<T, U>
2019-03-30 16:48:36 +00:00
where
T: tty::EventedPty + event::OnResize + Send + 'static,
U: EventListener + Send + 'static,
{
2020-05-05 22:50:23 +00:00
/// Create a new event loop.
pub fn new(
terminal: Arc<FairMutex<Term<U>>>,
event_proxy: U,
pty: T,
hold: bool,
ref_test: bool,
) -> EventLoop<T, U> {
2017-08-30 18:43:37 +00:00
let (tx, rx) = channel::channel();
EventLoop {
poll: mio::Poll::new().expect("create mio Poll"),
pty,
tx,
rx,
terminal,
event_proxy,
hold,
ref_test,
}
}
2017-08-30 18:43:37 +00:00
pub fn channel(&self) -> Sender<Msg> {
self.tx.clone()
}
2020-05-05 22:50:23 +00:00
/// Drain the channel.
///
/// Returns `false` when a shutdown message was received.
fn drain_recv_channel(&mut self, state: &mut State) -> bool {
while let Ok(msg) = self.rx.try_recv() {
match msg {
Msg::Input(input) => state.write_list.push_back(input),
Msg::Shutdown => return false,
Msg::Resize(size) => self.pty.on_resize(&size),
}
}
true
}
2020-05-05 22:50:23 +00:00
/// Returns a `bool` indicating whether or not the event loop should continue running.
#[inline]
fn channel_event(&mut self, token: mio::Token, state: &mut State) -> bool {
if !self.drain_recv_channel(state) {
return false;
}
self.poll
.reregister(&self.rx, token, Ready::readable(), PollOpt::edge() | PollOpt::oneshot())
.unwrap();
true
}
#[inline]
fn pty_read<X>(
2016-12-17 06:48:04 +00:00
&mut self,
state: &mut State,
buf: &mut [u8],
mut writer: Option<&mut X>,
) -> io::Result<()>
2019-03-30 16:48:36 +00:00
where
X: Write,
2016-12-17 06:48:04 +00:00
{
let mut processed = 0;
let mut terminal = None;
loop {
match self.pty.reader().read(&mut buf[..]) {
Ok(0) => break,
Ok(got) => {
// Record bytes read; used to limit time spent in pty_read.
processed += got;
// Send a copy of bytes read to a subscriber. Used for
// example with ref test recording.
writer = writer.map(|w| {
w.write_all(&buf[..got]).unwrap();
w
});
// Get reference to terminal. Lock is acquired on initial
// iteration and held until there's no bytes left to parse
2020-05-05 22:50:23 +00:00
// or we've reached `MAX_READ`.
if terminal.is_none() {
terminal = Some(self.terminal.lock());
}
let terminal = terminal.as_mut().unwrap();
2020-05-05 22:50:23 +00:00
// Run the parser.
for byte in &buf[..got] {
2019-03-30 16:48:36 +00:00
state.parser.advance(&mut **terminal, *byte, &mut self.pty.writer());
}
2020-05-05 22:50:23 +00:00
// Exit if we've processed enough bytes.
if processed > MAX_READ {
break;
}
2019-03-30 16:48:36 +00:00
},
Err(err) => match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => {
break;
2019-03-30 16:48:36 +00:00
},
_ => return Err(err),
},
}
}
if processed > 0 {
2020-05-05 22:50:23 +00:00
// Queue terminal redraw.
self.event_proxy.send_event(Event::Wakeup);
}
Ok(())
}
#[inline]
fn pty_write(&mut self, state: &mut State) -> io::Result<()> {
state.ensure_next();
'write_many: while let Some(mut current) = state.take_current() {
'write_one: loop {
match self.pty.writer().write(current.remaining_bytes()) {
Ok(0) => {
state.set_current(Some(current));
break 'write_many;
2019-03-30 16:48:36 +00:00
},
Ok(n) => {
current.advance(n);
if current.finished() {
state.goto_next();
break 'write_one;
}
2019-03-30 16:48:36 +00:00
},
Err(err) => {
state.set_current(Some(current));
match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many,
_ => return Err(err),
}
2019-03-30 16:48:36 +00:00
},
}
}
}
Ok(())
}
pub fn spawn(mut self) -> JoinHandle<(Self, State)> {
2020-05-05 22:50:23 +00:00
thread::spawn_named("PTY reader", move || {
let mut state = State::default();
let mut buf = [0u8; MAX_READ];
let mut tokens = (0..).map(Into::into);
let poll_opts = PollOpt::edge() | PollOpt::oneshot();
let channel_token = tokens.next().unwrap();
2019-03-30 16:48:36 +00:00
self.poll.register(&self.rx, channel_token, Ready::readable(), poll_opts).unwrap();
2020-05-05 22:50:23 +00:00
// Register TTY through EventedRW interface.
2019-03-30 16:48:36 +00:00
self.pty.register(&self.poll, &mut tokens, Ready::readable(), poll_opts).unwrap();
let mut events = Events::with_capacity(1024);
let mut pipe = if self.ref_test {
Some(File::create("./alacritty.recording").expect("create alacritty recording"))
} else {
None
};
'event_loop: loop {
if let Err(err) = self.poll.poll(&mut events, None) {
match err.kind() {
ErrorKind::Interrupted => continue,
_ => panic!("EventLoop polling error: {:?}", err),
}
}
for event in events.iter() {
match event.token() {
token if token == channel_token => {
if !self.channel_event(channel_token, &mut state) {
break 'event_loop;
}
},
token if token == self.pty.child_event_token() => {
if let Some(tty::ChildEvent::Exited) = self.pty.next_child_event() {
if self.hold {
// With hold enabled, make sure the PTY is drained.
let _ = self.pty_read(&mut state, &mut buf, pipe.as_mut());
} else {
// Without hold, shutdown the terminal.
self.terminal.lock().exit();
}
self.event_proxy.send_event(Event::Wakeup);
break 'event_loop;
}
},
2019-03-30 16:48:36 +00:00
token
if token == self.pty.read_token()
|| token == self.pty.write_token() =>
{
#[cfg(unix)]
if UnixReady::from(event.readiness()).is_hup() {
// Don't try to do I/O on a dead PTY.
continue;
}
if event.readiness().is_readable() {
2020-05-05 22:50:23 +00:00
if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut())
{
// On Linux, a `read` on the master side of a PTY can fail
// with `EIO` if the client side hangs up. In that case,
// just loop back round for the inevitable `Exited` event.
// This sucks, but checking the process is either racy or
// blocking.
2019-03-30 16:48:36 +00:00
#[cfg(target_os = "linux")]
if err.kind() == ErrorKind::Other {
continue;
}
2020-05-05 22:50:23 +00:00
error!("Error reading from PTY in event loop: {}", err);
break 'event_loop;
}
}
if event.readiness().is_writable() {
2020-05-05 22:50:23 +00:00
if let Err(err) = self.pty_write(&mut state) {
error!("Error writing to PTY in event loop: {}", err);
break 'event_loop;
}
}
}
_ => (),
}
}
2020-05-05 22:50:23 +00:00
// Register write interest if necessary.
let mut interest = Ready::readable();
if state.needs_write() {
interest.insert(Ready::writable());
}
2020-05-05 22:50:23 +00:00
// Reregister with new interest.
self.pty.reregister(&self.poll, interest, poll_opts).unwrap();
}
2020-05-05 22:50:23 +00:00
// The evented instances are not dropped here so deregister them explicitly.
let _ = self.poll.deregister(&self.rx);
let _ = self.pty.deregister(&self.poll);
(self, state)
})
}
}