diff --git a/src/gliumwindows.rs b/src/gliumwindows.rs index 5ef046ee2..c2bd3b1b4 100644 --- a/src/gliumwindows.rs +++ b/src/gliumwindows.rs @@ -13,12 +13,14 @@ use std::io::{Read, Write}; use std::process::Child; use std::process::Command; use std::rc::Rc; +use std::sync::mpsc::Receiver; use std::sync::mpsc::TryRecvError; use term::{self, Terminal}; use term::{MouseButton, MouseEventKind}; use term::KeyCode; use term::KeyModifiers; use term::hyperlink::Hyperlink; +use wakeup::WakeupMsg; struct Host { display: glium::Display, @@ -62,11 +64,13 @@ pub struct TerminalWindow { terminal: Terminal, process: Child, last_mouse_coords: (f64, f64), + wakeup_receiver: Receiver, } impl TerminalWindow { pub fn new( event_loop: &glutin::EventsLoop, + wakeup_receiver: Receiver, width: u16, height: u16, terminal: Terminal, @@ -116,6 +120,7 @@ impl TerminalWindow { terminal, process, last_mouse_coords: (0.0, 0.0), + wakeup_receiver, }) } @@ -439,8 +444,19 @@ impl TerminalWindow { Err(TryRecvError::Disconnected) => bail!("clipboard thread died"), } - self.try_read_pty()?; - self.test_for_child_exit()?; + loop { + match self.wakeup_receiver.try_recv() { + Ok(WakeupMsg::PtyReadable) => self.try_read_pty()?, + Ok(WakeupMsg::SigChld) => self.test_for_child_exit()?, + Ok(WakeupMsg::Paint) => if self.terminal.has_dirty_lines() { + self.paint()?; + }, + Err(_) => break, + } + } + } + Event::Suspended(suspended) => { + eprintln!("Suspended {:?}", suspended); } _ => {} } diff --git a/src/main.rs b/src/main.rs index b13edf2ca..90192d044 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,12 +38,15 @@ use std::os::unix::io::AsRawFd; use std::process::Command; use std::str; use std::thread; +use std::time::Duration; mod config; mod opengl; mod clipboard; +mod wakeup; +use wakeup::{Wakeup, WakeupMsg}; mod gliumwindows; mod font; @@ -81,12 +84,15 @@ fn run_glium( initial_pixel_height: u16, ) -> Result<(), Error> { let mut events_loop = glium::glutin::EventsLoop::new(); - sigchld::activate(events_loop.create_proxy())?; + + let (wakeup_receiver, wakeup) = Wakeup::new(events_loop.create_proxy()); + sigchld::activate(wakeup.clone())?; let master_fd = master.as_raw_fd(); let mut window = gliumwindows::TerminalWindow::new( &events_loop, + wakeup_receiver, initial_pixel_width, initial_pixel_height, terminal, @@ -100,7 +106,7 @@ fn run_glium( )?; { - let proxy = events_loop.create_proxy(); + let mut wakeup = wakeup.clone(); thread::spawn(move || { let poll = Poll::new().expect("mio Poll failed to init"); poll.register( @@ -112,12 +118,24 @@ fn run_glium( let mut events = Events::with_capacity(8); loop { - match poll.poll(&mut events, None) { - Ok(_) => for event in &events { + match poll.poll(&mut events, Some(Duration::from_millis(50))) { + Ok(n) if n > 0 => for event in &events { if event.token() == Token(0) && event.readiness().is_readable() { - proxy.wakeup().expect("failed to wake event loop"); + wakeup + .send(WakeupMsg::PtyReadable) + .expect("failed to wakeup gui thread"); } }, + Ok(_) => { + // Tick and wakeup the gui thread to ask it to render + // if needed. Without this we'd only repaint when + // the window system decides that we were damaged. + // We don't want to paint after every state change + // as that would be too frequent. + wakeup + .send(WakeupMsg::Paint) + .expect("failed to wakeup gui thread"); + } _ => {} } } @@ -125,12 +143,7 @@ fn run_glium( } events_loop.run_forever(|event| match window.dispatch_event(event) { - Ok(_) => { - if window.need_paint() { - window.paint().expect("paint failed"); - } - glium::glutin::ControlFlow::Continue - } + Ok(_) => glium::glutin::ControlFlow::Continue, Err(err) => { eprintln!("{:?}", err); glium::glutin::ControlFlow::Break diff --git a/src/sigchld.rs b/src/sigchld.rs index f711d13cc..3c39c94f1 100644 --- a/src/sigchld.rs +++ b/src/sigchld.rs @@ -1,28 +1,28 @@ //! Helper for detecting SIGCHLD use failure::Error; -use glium::glutin::EventsLoopProxy; use libc; use std::io; use std::mem; use std::ptr; +use wakeup::{Wakeup, WakeupMsg}; -static mut EVENT_LOOP: Option = None; +static mut EVENT_LOOP: Option = None; extern "C" fn chld_handler(_signo: libc::c_int, _si: *const libc::siginfo_t, _: *const u8) { unsafe { match EVENT_LOOP.as_mut() { - Some(proxy) => { - proxy.wakeup().ok(); + Some(wakeup) => { + wakeup.send(WakeupMsg::SigChld).ok(); } None => (), } } } -pub fn activate(proxy: EventsLoopProxy) -> Result<(), Error> { +pub fn activate(wakeup: Wakeup) -> Result<(), Error> { unsafe { - EVENT_LOOP = Some(proxy); + EVENT_LOOP = Some(wakeup); let mut sa: libc::sigaction = mem::zeroed(); sa.sa_sigaction = chld_handler as usize; diff --git a/src/wakeup.rs b/src/wakeup.rs new file mode 100644 index 000000000..f2e047aac --- /dev/null +++ b/src/wakeup.rs @@ -0,0 +1,28 @@ +use failure::Error; +use glium::glutin::EventsLoopProxy; +use std::sync::mpsc::{channel, Receiver, Sender}; + +#[derive(Debug)] +pub enum WakeupMsg { + PtyReadable, + SigChld, + Paint, +} + +#[derive(Clone)] +pub struct Wakeup { + sender: Sender, + proxy: EventsLoopProxy, +} + +impl Wakeup { + pub fn new(proxy: EventsLoopProxy) -> (Receiver, Self) { + let (sender, receiver) = channel(); + (receiver, Self { sender, proxy }) + } + pub fn send(&mut self, what: WakeupMsg) -> Result<(), Error> { + self.sender.send(what)?; + self.proxy.wakeup()?; + Ok(()) + } +}