diff --git a/src/gliumwindows.rs b/src/gliumwindows.rs index dadcd2b4cd7..1596d65e9ef 100644 --- a/src/gliumwindows.rs +++ b/src/gliumwindows.rs @@ -10,8 +10,7 @@ use guiloop::{GuiEventLoop, SessionTerminated}; use opengl::render::Renderer; use opengl::textureatlas::OutOfTextureSpace; use pty::MasterPty; -use std::io; -use std::io::{Read, Write}; +use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::process::{Child, Command}; use std::rc::Rc; @@ -207,19 +206,8 @@ impl TerminalWindow { } } - pub fn try_read_pty(&mut self) -> Result<(), Error> { - const BUFSIZE: usize = 8192; - let mut buf = [0; BUFSIZE]; - - match self.host.pty.read(&mut buf) { - Ok(size) => self.terminal.advance_bytes(&buf[0..size], &mut self.host), - Err(err) => { - if err.kind() != io::ErrorKind::WouldBlock { - return Err(SessionTerminated::Error { err: err.into() }.into()); - } - } - } - Ok(()) + pub fn process_data_read_from_pty(&mut self, data: &[u8]) { + self.terminal.advance_bytes(data, &mut self.host) } fn resize_surfaces(&mut self, width: u16, height: u16) -> Result { diff --git a/src/guiloop/glutinloop.rs b/src/guiloop/glutinloop.rs index bbb984df18a..d640690cca6 100644 --- a/src/guiloop/glutinloop.rs +++ b/src/guiloop/glutinloop.rs @@ -1,23 +1,22 @@ use failure::Error; -use futures::{future, Future}; +use futures::future; use glium; use glium::glutin::EventsLoopProxy; -use mio; -use mio::{PollOpt, Ready, Token}; use std::cell::RefCell; use std::collections::HashMap; +use std::io::{self, Read}; #[cfg(unix)] use std::os::unix::io::RawFd; use std::rc::Rc; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; -use std::time::Duration; +use std::thread; +use super::SessionTerminated; pub use glium::glutin::WindowId; pub use gliumwindows::TerminalWindow; use futurecore; use gliumwindows; -use remotemio; use sigchld; #[derive(Clone)] @@ -42,14 +41,31 @@ pub fn channel(proxy: EventsLoopProxy) -> (GuiSender, Receiver) { (GuiSender { tx, proxy }, rx) } +#[derive(Clone)] +enum IOEvent { + Data { window_id: WindowId, data: Vec }, + Terminated { window_id: WindowId }, +} + +struct ReadWrap { + fd: RawFd, +} +impl io::Read for ReadWrap { + fn read(&mut self, buf: &mut [u8]) -> Result { + let size = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut _, buf.len()) }; + if size == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(size as usize) + } + } +} + /// This struct holds references to Windows. /// The primary mapping is from `WindowId` -> `TerminalWindow`. -/// There is a secondary mapping from `RawFd` -> `WindowId` that -/// is used to process data to be read from the pty. #[derive(Default)] struct Windows { by_id: HashMap, - by_fd: HashMap, } /// The `GuiEventLoop` represents the combined gui event processor, @@ -60,8 +76,8 @@ pub struct GuiEventLoop { pub event_loop: RefCell, windows: Rc>, pub core: futurecore::Core, - poll: remotemio::IOMgr, - poll_rx: Receiver, + poll_tx: GuiSender, + poll_rx: Receiver, pub paster: GuiSender, paster_rx: Receiver, sigchld_rx: Receiver<()>, @@ -74,16 +90,15 @@ impl GuiEventLoop { let (fut_tx, fut_rx) = channel(event_loop.create_proxy()); let core = futurecore::Core::new(fut_tx, fut_rx); - let (wake_tx, poll_rx) = channel(event_loop.create_proxy()); + let (poll_tx, poll_rx) = channel(event_loop.create_proxy()); let (paster, paster_rx) = channel(event_loop.create_proxy()); let (sigchld_tx, sigchld_rx) = channel(event_loop.create_proxy()); - let poll = remotemio::IOMgr::new(Duration::from_millis(50), wake_tx); sigchld::activate(sigchld_tx)?; Ok(Self { core, - poll, + poll_tx, poll_rx, paster, paster_rx, @@ -99,10 +114,35 @@ impl GuiEventLoop { let fd = window.pty_fd(); let mut windows = self.windows.borrow_mut(); windows.by_id.insert(window_id, window); - windows.by_fd.insert(fd, window_id); - self.poll - .register(fd, Token(fd as usize), Ready::readable(), PollOpt::edge())? - .wait()??; + + let tx = self.poll_tx.clone(); + + thread::spawn(move || { + let mut fd = ReadWrap { fd }; + const BUFSIZE: usize = 8192; + let mut buf = [0; BUFSIZE]; + loop { + match fd.read(&mut buf) { + Ok(size) => { + if tx + .send(IOEvent::Data { + window_id, + data: buf[0..size].to_vec(), + }) + .is_err() + { + return; + } + } + Err(err) => { + eprintln!("window {:?} {:?}", window_id, err); + tx.send(IOEvent::Terminated { window_id }).ok(); + return; + } + } + } + }); + Ok(()) } @@ -118,7 +158,7 @@ impl GuiEventLoop { let dead = match self.windows.borrow_mut().by_id.get_mut(&window_id) { Some(window) => match window.dispatch_event(event) { Ok(_) => None, - Err(err) => match err.downcast_ref::() { + Err(err) => match err.downcast_ref::() { Some(_) => Some(window_id), _ => return Err(err), }, @@ -140,65 +180,17 @@ impl GuiEventLoop { /// Spawns a future that will gracefully shut down the resources associated /// with the specified window. fn schedule_window_close(&self, window_id: WindowId) -> Result<(), Error> { - let fd = { - let mut windows = self.windows.borrow_mut(); - - let window = windows.by_id.get_mut(&window_id).ok_or_else(|| { - format_err!("no window_id {:?} in the windows_by_id map", window_id) - })?; - window.pty_fd() - }; - let windows = Rc::clone(&self.windows); - self.core.spawn(self.poll.deregister(fd)?.then(move |_| { + self.core.spawn(futures::future::lazy(move || { let mut windows = windows.borrow_mut(); windows.by_id.remove(&window_id); - windows.by_fd.remove(&fd); future::ok(()) })); Ok(()) } - /// Process an even from the remote mio instance. - /// At this time, all such events correspond to readable events - /// for the pty associated with a window. - fn process_pty_event(&self, event: mio::Event) -> Result<(), Error> { - // The token is the fd - let fd = event.token().0 as RawFd; - - let (window_id, result) = { - let mut windows = self.windows.borrow_mut(); - - let window_id = windows - .by_fd - .get(&fd) - .ok_or_else(|| { - format_err!("fd {} has no associated window in windows_by_fd map", fd) - }) - .map(|w| *w)?; - - let window = windows.by_id.get_mut(&window_id).ok_or_else(|| { - format_err!( - "fd {} -> window_id {:?} but no associated window is in the windows_by_id map", - fd, - window_id - ) - })?; - (window_id, window.try_read_pty()) - }; - - if let Err(err) = result { - if err.downcast_ref::().is_some() { - self.schedule_window_close(window_id)?; - } else { - bail!("{:?}", err); - } - } - Ok(()) - } - /// Run through all of the windows and cause them to paint if they need it. /// This happens ~50ms or so. fn do_paint(&self) { @@ -213,13 +205,16 @@ impl GuiEventLoop { fn process_poll(&self) -> Result<(), Error> { loop { match self.poll_rx.try_recv() { - Ok(remotemio::Notification::EventReady(event)) => { - match self.process_pty_event(event) { - Ok(_) => {} - Err(err) => eprintln!("process_pty_event: {:?}", err), - } + Ok(IOEvent::Data { window_id, data }) => { + let mut windows = self.windows.borrow_mut(); + let window = windows.by_id.get_mut(&window_id).ok_or_else(|| { + format_err!("window_id {:?} not in windows_by_id map", window_id) + })?; + window.process_data_read_from_pty(&data); + } + Ok(IOEvent::Terminated { window_id }) => { + self.schedule_window_close(window_id)?; } - Ok(remotemio::Notification::IntervalDone) => self.do_paint(), Err(TryRecvError::Empty) => return Ok(()), Err(err) => bail!("poll_rx disconnected {:?}", err), } @@ -281,7 +276,10 @@ impl GuiEventLoop { let result = self.process_gui_event(&event); match result { - Ok(Continue) => Continue, + Ok(Continue) => { + self.do_paint(); + Continue + } Ok(Break) => Break, Err(err) => { eprintln!("Error in event loop: {:?}", err); @@ -313,7 +311,7 @@ impl GuiEventLoop { // are no windows left, then we are done. { let windows = self.windows.borrow(); - if windows.by_id.is_empty() && windows.by_fd.is_empty() { + if windows.by_id.is_empty() { debug!("No more windows; done!"); return Ok(()); } diff --git a/src/main.rs b/src/main.rs index f44d40e3858..cb7c9c66442 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,8 +51,6 @@ mod config; mod futurecore; mod opengl; -#[cfg(any(windows, feature = "force-glutin", target_os = "macos"))] -mod remotemio; mod clipboard; #[cfg(any(windows, feature = "force-glutin", target_os = "macos"))] diff --git a/src/remotemio.rs b/src/remotemio.rs deleted file mode 100644 index db76413aebd..00000000000 --- a/src/remotemio.rs +++ /dev/null @@ -1,195 +0,0 @@ -use failure::Error; -use futures::sync::oneshot; -use guiloop::GuiSender; -use mio::unix::EventedFd; -use mio::{Event, Evented, Events, Poll, PollOpt, Ready, Token}; -use mio_extras::channel::{channel as mio_channel, Receiver as MioReceiver, Sender as MioSender}; -use std::collections::HashMap; -use std::io; -use std::os::unix::io::RawFd; -use std::sync::mpsc::TryRecvError; -use std::sync::Arc; -use std::thread; -use std::time::{Duration, Instant}; - -enum Request { - Register { - fd: RawFd, - token: Token, - interest: Ready, - opts: PollOpt, - done: oneshot::Sender>, - }, - Deregister { - fd: RawFd, - done: oneshot::Sender>, - }, -} - -pub enum Notification { - EventReady(Event), - IntervalDone, -} - -struct Fd { - fd: RawFd, -} - -impl Evented for Fd { - fn register( - &self, - poll: &Poll, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> io::Result<()> { - EventedFd(&self.fd).register(poll, token, interest, opts) - } - - fn reregister( - &self, - poll: &Poll, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> io::Result<()> { - EventedFd(&self.fd).reregister(poll, token, interest, opts) - } - - fn deregister(&self, poll: &Poll) -> io::Result<()> { - EventedFd(&self.fd).deregister(poll) - } -} - -struct Inner { - rx: MioReceiver, - interval: Duration, - wakeup: GuiSender, - fd_map: HashMap>, -} - -/// The `IOMgr` represents a mio `Poll` instance that is driven by a separate -/// thread. Unix/X11 systems don't really need this to be in a separate -/// thread, but for the sake of minimizing platform differences we do use the -/// same approach for all platforms. `IOMgr` offers `register` and `deregister` -/// methods that are similar to their namesakes in `Poll`, except that `IOMgr` -/// returns `Future` instances that can be used to asynchronously handle the -/// result of those operations. -pub struct IOMgr { - tx: MioSender, -} - -impl IOMgr { - pub fn new(interval: Duration, wakeup: GuiSender) -> Self { - let (tx, rx) = mio_channel(); - let mut inner = Inner { - rx, - interval, - wakeup, - fd_map: HashMap::new(), - }; - thread::spawn(move || match inner.run() { - Ok(_) => eprintln!("IOMgr thread completed with success"), - Err(err) => eprintln!("IOMgr thread failed: {:?}", err), - }); - Self { tx } - } - - pub fn register( - &self, - fd: RawFd, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> Result>, Error> { - let (done, rx) = oneshot::channel(); - self.tx.send(Request::Register { - fd, - token, - interest, - opts, - done, - })?; - Ok(rx) - } - - pub fn deregister(&self, fd: RawFd) -> Result>, Error> { - let (done, rx) = oneshot::channel(); - self.tx.send(Request::Deregister { fd, done })?; - Ok(rx) - } -} - -impl Inner { - fn dereg(&mut self, poll: &Poll, fd: RawFd) -> Result<(), io::Error> { - let evented = self - .fd_map - .get(&fd) - .ok_or_else(|| { - io::Error::new( - io::ErrorKind::Other, - format!("fd {} is not present in IOMgr fd_map", fd), - ) - })? - .clone(); - - poll.deregister(&*evented)?; - - self.fd_map.remove(&fd); - Ok(()) - } - - fn run(&mut self) -> Result<(), Error> { - let poll = Poll::new()?; - poll.register(&self.rx, Token(0), Ready::readable(), PollOpt::level())?; - - let mut events = Events::with_capacity(8); - let mut last_interval = Instant::now(); - - loop { - let now = Instant::now(); - let diff = now - last_interval; - let period = if diff >= self.interval { - self.wakeup.send(Notification::IntervalDone)?; - last_interval = now; - self.interval - } else { - self.interval - diff - }; - - if poll.poll(&mut events, Some(period)).is_ok() { - for event in &events { - if event.token() == Token(0) { - match self.rx.try_recv() { - Err(TryRecvError::Empty) => {} - Err(err) => bail!("IOMgr: disconnected {:?}", err), - Ok(Request::Register { - fd, - token, - interest, - opts, - done, - }) => { - let evented = Arc::new(Fd { fd }); - self.fd_map.insert(fd, evented.clone()); - - match done.send(poll.register(&*evented, token, interest, opts)) { - Ok(_) => {} - Err(err) => eprintln!("done channel went away {:?}", err), - }; - } - Ok(Request::Deregister { fd, done }) => { - match done.send(self.dereg(&poll, fd)) { - Ok(_) => eprintln!("deregistered fd {}", fd), - Err(err) => eprintln!("done channel went away {:?}", err), - }; - } - } - } else { - self.wakeup.send(Notification::EventReady(event))?; - } - } - } - } - } -}