diff options
author | Zac Pullar-Strecker <zacps@users.noreply.github.com> | 2018-10-17 06:02:52 +1300 |
---|---|---|
committer | Joe Wilm <jwilm@users.noreply.github.com> | 2018-10-16 10:02:52 -0700 |
commit | 15e0deae2b49078b47a782679300cdf99d9ce687 (patch) | |
tree | 8175fbed0def1af08cd2db41583975adbb27dff1 /src/event_loop.rs | |
parent | b41c6b736d67d61e92b174dfea58ae46813934cd (diff) | |
download | alacritty-15e0deae2b49078b47a782679300cdf99d9ce687.tar.gz alacritty-15e0deae2b49078b47a782679300cdf99d9ce687.zip |
Add support for Windows (#1374)
Initial support for Windows is implemented using the winpty translation
layer. Clipboard support for Windows is provided through the `clipboard`
crate, and font rasterization is provided by RustType.
The tty.rs file has been split into OS-specific files to separate
standard pty handling from the winpty implementation.
Several binary components are fetched via build script on windows
including libclang and winpty. These could be integrated more directly
in the future either by building those dependencies as part of the
Alacritty build process or by leveraging git lfs to store the artifacts.
Fixes #28.
Diffstat (limited to 'src/event_loop.rs')
-rw-r--r-- | src/event_loop.rs | 186 |
1 files changed, 87 insertions, 99 deletions
diff --git a/src/event_loop.rs b/src/event_loop.rs index d7d27243..8cc094f6 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -1,20 +1,21 @@ //! The main event loop which performs I/O on the pseudoterminal use std::borrow::Cow; use std::collections::VecDeque; -use std::io::{self, ErrorKind, Write}; +use std::io::{self, ErrorKind, Read, Write}; use std::fs::File; -use std::os::unix::io::AsRawFd; use std::sync::Arc; +use std::marker::Send; use mio::{self, Events, PollOpt, Ready}; -#[cfg(unix)] +use mio_more::channel::{self, Receiver, Sender}; + +#[cfg(not(windows))] use mio::unix::UnixReady; -use mio::unix::EventedFd; -use mio_more::channel::{self, Sender, Receiver}; use ansi; use display; use event; +use tty; use term::Term; use util::thread; use sync::FairMutex; @@ -26,16 +27,16 @@ pub enum Msg { Input(Cow<'static, [u8]>), /// Indicates that the `EventLoop` should shut down, as Alacritty is shutting down - Shutdown + Shutdown, } /// The main event!.. loop. /// /// Handles all the pty I/O and runs the pty parser which updates terminal /// state. -pub struct EventLoop<Io> { +pub struct EventLoop<T: tty::EventedReadWrite> { poll: mio::Poll, - pty: Io, + pty: T, rx: Receiver<Msg>, tx: Sender<Msg>, terminal: Arc<FairMutex<Term>>, @@ -83,7 +84,7 @@ pub struct Notifier(pub Sender<Msg>); impl event::Notify for Notifier { fn notify<B>(&mut self, bytes: B) - where B: Into<Cow<'static, [u8]>> + where B: Into<Cow<'static, [u8]>>, { let bytes = bytes.into(); // terminal hangs if we send 0 bytes through. @@ -96,7 +97,6 @@ impl event::Notify for Notifier { } } - impl Default for State { fn default() -> State { State { @@ -163,19 +163,17 @@ impl Writing { /// `mio::Token` for the event loop channel const CHANNEL: mio::Token = mio::Token(0); -/// `mio::Token` for the pty file descriptor -const PTY: mio::Token = mio::Token(1); - -impl<Io> EventLoop<Io> - where Io: io::Read + io::Write + Send + AsRawFd + 'static +impl<T> EventLoop<T> + where + T: tty::EventedReadWrite + Send + 'static, { /// Create a new event loop pub fn new( terminal: Arc<FairMutex<Term>>, display: display::Notifier, - pty: Io, + pty: T, ref_test: bool, - ) -> EventLoop<Io> { + ) -> EventLoop<T> { let (tx, rx) = channel::channel(); EventLoop { poll: mio::Poll::new().expect("create mio Poll"), @@ -203,7 +201,7 @@ impl<Io> EventLoop<Io> match msg { Msg::Input(input) => { state.write_list.push_back(input); - }, + } Msg::Shutdown => { return DrainResult::Shutdown; } @@ -224,32 +222,22 @@ impl<Io> EventLoop<Io> return false; } - self.poll.reregister( - &self.rx, CHANNEL, - Ready::readable(), - PollOpt::edge() | PollOpt::oneshot() - ).expect("reregister channel"); - - if state.needs_write() { - self.poll.reregister( - &EventedFd(&self.pty.as_raw_fd()), - PTY, - Ready::readable() | Ready::writable(), - PollOpt::edge() | PollOpt::oneshot() - ).expect("reregister fd after channel recv"); - } + self.poll + .reregister(&self.rx, CHANNEL, Ready::readable(), PollOpt::edge() | PollOpt::oneshot()) + .unwrap(); true } #[inline] - fn pty_read<W>( + fn pty_read<X>( &mut self, state: &mut State, buf: &mut [u8], - mut writer: Option<&mut W> + mut writer: Option<&mut X>, ) -> io::Result<()> - where W: Write + where + X: Write, { const MAX_READ: usize = 0x1_0000; let mut processed = 0; @@ -259,7 +247,7 @@ impl<Io> EventLoop<Io> let mut send_wakeup = false; loop { - match self.pty.read(&mut buf[..]) { + match self.pty.reader().read(&mut buf[..]) { Ok(0) => break, Ok(got) => { // Record bytes read; used to limit time spent in pty_read. @@ -286,23 +274,22 @@ impl<Io> EventLoop<Io> // Run the parser for byte in &buf[..got] { - state.parser.advance(&mut **terminal, *byte, &mut self.pty); + state + .parser + .advance(&mut **terminal, *byte, &mut self.pty.writer()); } // Exit if we've processed enough bytes if processed > MAX_READ { break; } - }, - Err(err) => { - match err.kind() { - ErrorKind::Interrupted | - ErrorKind::WouldBlock => { - break; - }, - _ => return Err(err), - } } + Err(err) => match err.kind() { + ErrorKind::Interrupted | ErrorKind::WouldBlock => { + break; + } + _ => return Err(err), + }, } } @@ -323,56 +310,53 @@ impl<Io> EventLoop<Io> 'write_many: while let Some(mut current) = state.take_current() { 'write_one: loop { - match self.pty.write(current.remaining_bytes()) { + match self.pty.writer().write(current.remaining_bytes()) { Ok(0) => { state.set_current(Some(current)); break 'write_many; - }, + } Ok(n) => { current.advance(n); if current.finished() { state.goto_next(); break 'write_one; } - }, + } Err(err) => { state.set_current(Some(current)); match err.kind() { - ErrorKind::Interrupted | - ErrorKind::WouldBlock => break 'write_many, + ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many, _ => return Err(err), } } } - } } Ok(()) } - pub fn spawn( - mut self, - state: Option<State> - ) -> thread::JoinHandle<(EventLoop<Io>, State)> { + pub fn spawn(mut self, state: Option<State>) -> thread::JoinHandle<(Self, State)> { thread::spawn_named("pty reader", move || { let mut state = state.unwrap_or_else(Default::default); let mut buf = [0u8; 0x1000]; - let fd = self.pty.as_raw_fd(); - let fd = EventedFd(&fd); - let poll_opts = PollOpt::edge() | PollOpt::oneshot(); - self.poll.register(&self.rx, CHANNEL, Ready::readable(), poll_opts).unwrap(); - self.poll.register(&fd, PTY, Ready::readable(), poll_opts).unwrap(); + let tokens = [1, 2]; + + self.poll + .register(&self.rx, CHANNEL, Ready::readable(), poll_opts) + .unwrap(); + + // Register TTY through EventedRW interface + self.pty + .register(&self.poll, &mut tokens.iter(), Ready::readable(), poll_opts).unwrap(); let mut events = Events::with_capacity(1024); let mut pipe = if self.ref_test { - let file = File::create("./alacritty.recording") - .expect("create alacritty recording"); - Some(file) + Some(File::create("./alacritty.recording").expect("create alacritty recording")) } else { None }; @@ -381,64 +365,68 @@ impl<Io> EventLoop<Io> if let Err(err) = self.poll.poll(&mut events, None) { match err.kind() { ErrorKind::Interrupted => continue, - _ => panic!("EventLoop polling error: {:?}", err) + _ => panic!("EventLoop polling error: {:?}", err), } } for event in events.iter() { match event.token() { - CHANNEL => { - if !self.channel_event(&mut state) { - break 'event_loop; - } + CHANNEL => if !self.channel_event(&mut state) { + break 'event_loop; }, - PTY => { - let ready = event.readiness(); - - #[cfg(unix)] { - if UnixReady::from(ready).is_hup() { - break 'event_loop; - } - } - - if ready.is_readable() { - if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) { - error!("Event loop exiting due to error: {} [{}:{}]", - err, file!(), line!()); - break 'event_loop; + token if token == self.pty.read_token() || token == self.pty.write_token() => { + #[cfg(unix)] + { + if UnixReady::from(event.readiness()).is_hup() { + break 'event_loop; + } } + if event.readiness().is_readable() { + if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) + { + error!( + "Event loop exitting due to error: {} [{}:{}]", + err, + file!(), + line!() + ); + break 'event_loop; + } if ::tty::process_should_exit() { break 'event_loop; } } - if ready.is_writable() { + if event.readiness().is_writable() { if let Err(err) = self.pty_write(&mut state) { - error!("Event loop exiting due to error: {} [{}:{}]", - err, file!(), line!()); + error!( + "Event loop exitting due to error: {} [{}:{}]", + err, + file!(), + line!() + ); break 'event_loop; } } - - // Figure out pty interest - let mut interest = Ready::readable(); - if state.needs_write() { - interest.insert(Ready::writable()); - } - - // Reregister pty - self.poll - .reregister(&fd, PTY, interest, poll_opts) - .expect("register fd after read/write"); - }, + } _ => (), } } + + // Register write interest if necessary + let mut interest = Ready::readable(); + if state.needs_write() { + interest.insert(Ready::writable()); + } + // Reregister with new interest + self.pty.reregister(&self.poll, interest, poll_opts).unwrap(); } + // The evented instances are not dropped here so deregister them explicitly + // TODO: Is this still necessary? let _ = self.poll.deregister(&self.rx); - let _ = self.poll.deregister(&fd); + self.pty.deregister(&self.poll).unwrap(); (self, state) }) |