mirror of
https://github.com/wez/wezterm.git
synced 2024-12-26 23:04:49 +03:00
use futures to manage closing a window
This commit is contained in:
parent
5db84856e9
commit
ce8de2ca03
@ -13,12 +13,14 @@
|
|||||||
use futures::{Async, Future};
|
use futures::{Async, Future};
|
||||||
use futures::executor::{self, Notify, Spawn};
|
use futures::executor::{self, Notify, Spawn};
|
||||||
use futures::future::{ExecuteError, Executor};
|
use futures::future::{ExecuteError, Executor};
|
||||||
|
use glium::glutin::EventsLoopProxy;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use wakeup::{channel, GuiSender};
|
||||||
|
|
||||||
pub struct Core {
|
pub struct Core {
|
||||||
tx: mpsc::Sender<usize>,
|
tx: GuiSender<usize>,
|
||||||
rx: mpsc::Receiver<usize>,
|
rx: mpsc::Receiver<usize>,
|
||||||
notify: Arc<Notifier>,
|
notify: Arc<Notifier>,
|
||||||
// Slab of running futures used to track what's running and what slots are
|
// Slab of running futures used to track what's running and what slots are
|
||||||
@ -34,8 +36,8 @@ enum Slot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new() -> Self {
|
pub fn new(proxy: EventsLoopProxy) -> Self {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = channel(proxy);
|
||||||
Self {
|
Self {
|
||||||
notify: Arc::new(Notifier {
|
notify: Arc::new(Notifier {
|
||||||
tx: Mutex::new(tx.clone()),
|
tx: Mutex::new(tx.clone()),
|
||||||
@ -128,7 +130,7 @@ struct Notifier {
|
|||||||
// itself is basically `Sync` as-is. Ideally this'd use something like
|
// itself is basically `Sync` as-is. Ideally this'd use something like
|
||||||
// an off-the-shelf mpsc queue as well as `thread::park` and
|
// an off-the-shelf mpsc queue as well as `thread::park` and
|
||||||
// `Thread::unpark`.
|
// `Thread::unpark`.
|
||||||
tx: Mutex<mpsc::Sender<usize>>,
|
tx: Mutex<GuiSender<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Notify for Notifier {
|
impl Notify for Notifier {
|
||||||
|
166
src/main.rs
166
src/main.rs
@ -36,15 +36,16 @@ extern crate xcb;
|
|||||||
#[cfg(all(unix, not(target_os = "macos")))]
|
#[cfg(all(unix, not(target_os = "macos")))]
|
||||||
extern crate xcb_util;
|
extern crate xcb_util;
|
||||||
|
|
||||||
use futures::Future;
|
use futures::{future, Future};
|
||||||
use glium::glutin::WindowId;
|
use glium::glutin::WindowId;
|
||||||
use mio::{PollOpt, Ready, Token};
|
use mio::{PollOpt, Ready, Token};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::mpsc::{Receiver, TryRecvError};
|
use std::sync::mpsc::{Receiver, TryRecvError};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -85,11 +86,15 @@ fn get_shell() -> Result<String, Error> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Windows {
|
||||||
|
by_id: HashMap<WindowId, gliumwindows::TerminalWindow>,
|
||||||
|
by_fd: HashMap<RawFd, WindowId>,
|
||||||
|
}
|
||||||
|
|
||||||
struct GuiEventLoop {
|
struct GuiEventLoop {
|
||||||
event_loop: RefCell<glium::glutin::EventsLoop>,
|
event_loop: RefCell<glium::glutin::EventsLoop>,
|
||||||
windows_by_id: RefCell<HashMap<WindowId, gliumwindows::TerminalWindow>>,
|
windows: Rc<RefCell<Windows>>,
|
||||||
windows_by_fd: RefCell<HashMap<RawFd, WindowId>>,
|
|
||||||
terminate: RefCell<bool>,
|
|
||||||
core: futurecore::Core,
|
core: futurecore::Core,
|
||||||
poll: remotemio::IOMgr,
|
poll: remotemio::IOMgr,
|
||||||
poll_rx: Receiver<remotemio::Notification>,
|
poll_rx: Receiver<remotemio::Notification>,
|
||||||
@ -101,7 +106,7 @@ struct GuiEventLoop {
|
|||||||
impl GuiEventLoop {
|
impl GuiEventLoop {
|
||||||
fn new() -> Result<Self, Error> {
|
fn new() -> Result<Self, Error> {
|
||||||
let event_loop = glium::glutin::EventsLoop::new();
|
let event_loop = glium::glutin::EventsLoop::new();
|
||||||
let core = futurecore::Core::new();
|
let core = futurecore::Core::new(event_loop.create_proxy());
|
||||||
|
|
||||||
let (wake_tx, poll_rx) = wakeup::channel(event_loop.create_proxy());
|
let (wake_tx, poll_rx) = wakeup::channel(event_loop.create_proxy());
|
||||||
let (paster, paster_rx) = wakeup::channel(event_loop.create_proxy());
|
let (paster, paster_rx) = wakeup::channel(event_loop.create_proxy());
|
||||||
@ -118,17 +123,16 @@ impl GuiEventLoop {
|
|||||||
paster_rx,
|
paster_rx,
|
||||||
sigchld_rx,
|
sigchld_rx,
|
||||||
event_loop: RefCell::new(event_loop),
|
event_loop: RefCell::new(event_loop),
|
||||||
windows_by_id: RefCell::new(HashMap::new()),
|
windows: Rc::new(RefCell::new(Default::default())),
|
||||||
windows_by_fd: RefCell::new(HashMap::new()),
|
|
||||||
terminate: RefCell::new(false),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_window(&self, window: gliumwindows::TerminalWindow) -> Result<(), Error> {
|
fn add_window(&self, window: gliumwindows::TerminalWindow) -> Result<(), Error> {
|
||||||
let window_id = window.window_id();
|
let window_id = window.window_id();
|
||||||
let fd = window.pty_fd();
|
let fd = window.pty_fd();
|
||||||
self.windows_by_id.borrow_mut().insert(window_id, window);
|
let mut windows = self.windows.borrow_mut();
|
||||||
self.windows_by_fd.borrow_mut().insert(fd, window_id);
|
windows.by_id.insert(window_id, window);
|
||||||
|
windows.by_fd.insert(fd, window_id);
|
||||||
self.poll
|
self.poll
|
||||||
.register(fd, Token(fd as usize), Ready::readable(), PollOpt::edge())?
|
.register(fd, Token(fd as usize), Ready::readable(), PollOpt::edge())?
|
||||||
.wait()??;
|
.wait()??;
|
||||||
@ -138,67 +142,86 @@ impl GuiEventLoop {
|
|||||||
fn process_gui_event(
|
fn process_gui_event(
|
||||||
&self,
|
&self,
|
||||||
event: &glium::glutin::Event,
|
event: &glium::glutin::Event,
|
||||||
dead_windows: &mut HashSet<WindowId>,
|
|
||||||
) -> Result<glium::glutin::ControlFlow, Error> {
|
) -> Result<glium::glutin::ControlFlow, Error> {
|
||||||
use glium::glutin::ControlFlow::{Break, Continue};
|
use glium::glutin::ControlFlow::{Break, Continue};
|
||||||
use glium::glutin::Event;
|
use glium::glutin::Event;
|
||||||
let result = match *event {
|
let result = match *event {
|
||||||
Event::WindowEvent { window_id, .. } => {
|
Event::WindowEvent { window_id, .. } => {
|
||||||
match self.windows_by_id.borrow_mut().get_mut(&window_id) {
|
let dead = match self.windows.borrow_mut().by_id.get_mut(&window_id) {
|
||||||
Some(window) => match window.dispatch_event(event) {
|
Some(window) => match window.dispatch_event(event) {
|
||||||
Ok(_) => Continue,
|
Ok(_) => None,
|
||||||
Err(err) => match err.downcast_ref::<gliumwindows::SessionTerminated>() {
|
Err(err) => match err.downcast_ref::<gliumwindows::SessionTerminated>() {
|
||||||
Some(_) => {
|
Some(_) => Some(window_id),
|
||||||
dead_windows.insert(window_id);
|
_ => return Err(err),
|
||||||
Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
eprintln!("{:?}", err);
|
|
||||||
Break
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
None => {
|
None => None,
|
||||||
// This happens surprisingly often!
|
};
|
||||||
// eprintln!("window event for unknown {:?}", window_id);
|
|
||||||
|
if let Some(window_id) = dead {
|
||||||
|
self.schedule_window_close(window_id)?;
|
||||||
|
}
|
||||||
Continue
|
Continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Awakened => Break,
|
Event::Awakened => Break,
|
||||||
_ => Continue,
|
_ => Continue,
|
||||||
};
|
};
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 |_| {
|
||||||
|
println!("done dereg");
|
||||||
|
let mut windows = windows.borrow_mut();
|
||||||
|
windows.by_id.remove(&window_id);
|
||||||
|
windows.by_fd.remove(&fd);
|
||||||
|
future::ok(())
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn process_pty_event(&self, event: mio::Event) -> Result<(), Error> {
|
fn process_pty_event(&self, event: mio::Event) -> Result<(), Error> {
|
||||||
// The token is the fd
|
// The token is the fd
|
||||||
let fd = event.token().0 as RawFd;
|
let fd = event.token().0 as RawFd;
|
||||||
|
|
||||||
let mut by_fd = self.windows_by_fd.borrow_mut();
|
let (window_id, result) = {
|
||||||
let result = {
|
let mut windows = self.windows.borrow_mut();
|
||||||
let window_id = by_fd.get(&fd).ok_or_else(|| {
|
|
||||||
format_err!("fd {} has no associated window in windows_by_fd map", fd)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut by_id = self.windows_by_id.borrow_mut();
|
let window_id = windows
|
||||||
let window = by_id.get_mut(window_id).ok_or_else(|| {
|
.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!(
|
format_err!(
|
||||||
"fd {} -> window_id {:?} but no associated window is in the windows_by_id map",
|
"fd {} -> window_id {:?} but no associated window is in the windows_by_id map",
|
||||||
fd,
|
fd,
|
||||||
window_id
|
window_id
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
window.try_read_pty()
|
(window_id, window.try_read_pty())
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(err) => match err.downcast_ref::<gliumwindows::SessionTerminated>() {
|
Err(err) => match err.downcast_ref::<gliumwindows::SessionTerminated>() {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
eprintln!("shutting down pty: {:?}", err);
|
self.schedule_window_close(window_id)?;
|
||||||
self.poll.deregister(fd)?.wait()??;
|
|
||||||
by_fd.remove(&fd);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -209,7 +232,7 @@ impl GuiEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn do_paint(&self) {
|
fn do_paint(&self) {
|
||||||
for (_, window) in self.windows_by_id.borrow_mut().iter_mut() {
|
for window in &mut self.windows.borrow_mut().by_id.values_mut() {
|
||||||
window.paint_if_needed().unwrap();
|
window.paint_if_needed().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,8 +257,9 @@ impl GuiEventLoop {
|
|||||||
loop {
|
loop {
|
||||||
match self.paster_rx.try_recv() {
|
match self.paster_rx.try_recv() {
|
||||||
Ok(window_id) => {
|
Ok(window_id) => {
|
||||||
self.windows_by_id
|
self.windows
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
.by_id
|
||||||
.get_mut(&window_id)
|
.get_mut(&window_id)
|
||||||
.map(|w| w.process_clipboard());
|
.map(|w| w.process_clipboard());
|
||||||
}
|
}
|
||||||
@ -245,30 +269,36 @@ impl GuiEventLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_sigchld_wakeups(&self, dead_windows: &mut HashSet<WindowId>) -> Result<(), Error> {
|
fn process_sigchld_wakeups(&self) -> Result<(), Error> {
|
||||||
loop {
|
loop {
|
||||||
match self.sigchld_rx.try_recv() {
|
match self.sigchld_rx.try_recv() {
|
||||||
Ok(_) => for (window_id, window) in self.windows_by_id.borrow_mut().iter_mut() {
|
Ok(_) => {
|
||||||
match window.test_for_child_exit() {
|
let window_ids: Vec<WindowId> = self.windows
|
||||||
Ok(_) => {}
|
.borrow_mut()
|
||||||
Err(err) => {
|
.by_id
|
||||||
eprintln!("pty finished: {:?}", err);
|
.iter_mut()
|
||||||
dead_windows.insert(window_id.clone());
|
.filter_map(|(window_id, window)| match window.test_for_child_exit() {
|
||||||
|
Ok(_) => None,
|
||||||
|
Err(_) => Some(*window_id),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for window_id in window_ids {
|
||||||
|
self.schedule_window_close(window_id)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(TryRecvError::Empty) => return Ok(()),
|
Err(TryRecvError::Empty) => return Ok(()),
|
||||||
Err(err) => bail!("paster_rx disconnected {:?}", err),
|
Err(err) => bail!("paster_rx disconnected {:?}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_event_loop(&self, mut dead_windows: &mut HashSet<WindowId>) -> Result<(), Error> {
|
fn run_event_loop(&self) -> Result<(), Error> {
|
||||||
let mut event_loop = self.event_loop.borrow_mut();
|
let mut event_loop = self.event_loop.borrow_mut();
|
||||||
event_loop.run_forever(|event| {
|
event_loop.run_forever(|event| {
|
||||||
use glium::glutin::ControlFlow::{Break, Continue};
|
use glium::glutin::ControlFlow::{Break, Continue};
|
||||||
|
|
||||||
let result = self.process_gui_event(&event, &mut dead_windows);
|
let result = self.process_gui_event(&event);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Continue) => Continue,
|
Ok(Continue) => Continue,
|
||||||
@ -282,24 +312,32 @@ impl GuiEventLoop {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_futures(&self) {
|
||||||
|
loop {
|
||||||
|
if !self.core.turn() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("did one future");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run(&self) -> Result<(), Error> {
|
fn run(&self) -> Result<(), Error> {
|
||||||
while !*self.terminate.borrow() {
|
loop {
|
||||||
let mut dead_windows = HashSet::new();
|
self.process_futures();
|
||||||
|
|
||||||
|
{
|
||||||
|
let windows = self.windows.borrow();
|
||||||
|
if windows.by_id.is_empty() && windows.by_fd.is_empty() {
|
||||||
|
eprintln!("No more windows; done!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.run_event_loop()?;
|
||||||
self.process_wakeups_new()?;
|
self.process_wakeups_new()?;
|
||||||
self.process_paste_wakeups()?;
|
self.process_paste_wakeups()?;
|
||||||
self.process_sigchld_wakeups(&mut dead_windows)?;
|
self.process_sigchld_wakeups()?;
|
||||||
self.run_event_loop(&mut dead_windows)?;
|
|
||||||
|
|
||||||
for window_id in dead_windows {
|
|
||||||
self.windows_by_id.borrow_mut().remove(&window_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.windows_by_id.borrow().len() == 0 {
|
|
||||||
// If we have no more windows left to manage, we're done
|
|
||||||
*self.terminate.borrow_mut() = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user