mirror of
https://github.com/wez/wezterm.git
synced 2024-11-22 22:42:48 +03:00
remove futurecore
This commit is contained in:
parent
12e93fce57
commit
4b4be6ac21
@ -9,7 +9,6 @@ bitflags = "~1.0"
|
|||||||
euclid = "~0.19"
|
euclid = "~0.19"
|
||||||
failure = "~0.1"
|
failure = "~0.1"
|
||||||
failure_derive = "~0.1"
|
failure_derive = "~0.1"
|
||||||
futures = "~0.1"
|
|
||||||
gl = "~0.11"
|
gl = "~0.11"
|
||||||
libc = "~0.2"
|
libc = "~0.2"
|
||||||
palette = "~0.4"
|
palette = "~0.4"
|
||||||
|
@ -5,10 +5,16 @@ use std::sync::{Arc, Condvar, Mutex};
|
|||||||
type NextFunc<T> = SendBoxFnOnce<'static, (Result<T, Error>,)>;
|
type NextFunc<T> = SendBoxFnOnce<'static, (Result<T, Error>,)>;
|
||||||
pub type SpawnFunc = SendBoxFnOnce<'static, ()>;
|
pub type SpawnFunc = SendBoxFnOnce<'static, ()>;
|
||||||
|
|
||||||
pub trait Executor {
|
pub trait Executor: Sync + Send {
|
||||||
fn execute(&self, f: SpawnFunc);
|
fn execute(&self, f: SpawnFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Executor for Arc<Executor> {
|
||||||
|
fn execute(&self, f: SpawnFunc) {
|
||||||
|
Executor::execute(&**self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An executor for spawning futures into the rayon global
|
/// An executor for spawning futures into the rayon global
|
||||||
/// thread pool
|
/// thread pool
|
||||||
pub struct RayonExecutor {}
|
pub struct RayonExecutor {}
|
||||||
|
@ -1,203 +0,0 @@
|
|||||||
//! A fairly simple executor for futures that run on the GUI thread.
|
|
||||||
//! Ideally we'd use something like a tokio core for this, but that
|
|
||||||
//! would only work on X11 systems and we'd need to subvert more
|
|
||||||
//! of the winit event loop driving.
|
|
||||||
//! Instead we use winit Awakened events to fall out of the blocking
|
|
||||||
//! gui loop and tend to the queued futures.
|
|
||||||
//! The core dispatching portion of this code is derived from one
|
|
||||||
//! of the test cases in the futures-rs library which is licensed
|
|
||||||
//! under the MIT license and has this copyright:
|
|
||||||
//! Copyright (c) 2016 Alex Crichton
|
|
||||||
//! Copyright (c) 2017 The Tokio Authors
|
|
||||||
|
|
||||||
use failure::Error;
|
|
||||||
use futures::executor::{self, Notify, Spawn};
|
|
||||||
use futures::future::{ExecuteError, Executor};
|
|
||||||
use futures::{Async, Future};
|
|
||||||
use std::cell::{Cell, RefCell};
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
const EXTERNAL_SPAWN: usize = !0usize;
|
|
||||||
|
|
||||||
pub trait CoreSender: Send {
|
|
||||||
fn send(&self, idx: usize) -> Result<(), Error>;
|
|
||||||
fn clone_sender(&self) -> Box<CoreSender>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CoreReceiver {
|
|
||||||
fn try_recv(&self) -> Result<usize, mpsc::TryRecvError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Core {
|
|
||||||
tx: Box<CoreSender>,
|
|
||||||
rx: Box<CoreReceiver>,
|
|
||||||
notify: Arc<Notifier>,
|
|
||||||
// Slab of running futures used to track what's running and what slots are
|
|
||||||
// empty. Slot indexes are then sent along tx/rx above to indicate which
|
|
||||||
// future is ready to get polled.
|
|
||||||
tasks: RefCell<Vec<Slot>>,
|
|
||||||
next_vacant: Cell<usize>,
|
|
||||||
external_spawn: Arc<SpawningFromAnotherThread>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SpawningFromAnotherThread {
|
|
||||||
futures: Mutex<VecDeque<Box<Future<Item = (), Error = ()> + Send>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Spawner {
|
|
||||||
tx: Box<CoreSender>,
|
|
||||||
external_spawn: Arc<SpawningFromAnotherThread>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Spawner {
|
|
||||||
pub fn spawn(&self, future: Box<dyn Future<Item = (), Error = ()> + Send>) {
|
|
||||||
let mut futures = self.external_spawn.futures.lock().unwrap();
|
|
||||||
futures.push_back(future);
|
|
||||||
self.tx.send(EXTERNAL_SPAWN).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Spawner {
|
|
||||||
fn clone(&self) -> Spawner {
|
|
||||||
Spawner {
|
|
||||||
tx: self.tx.clone_sender(),
|
|
||||||
external_spawn: Arc::clone(&self.external_spawn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Slot {
|
|
||||||
Vacant { next_vacant: usize },
|
|
||||||
RunningFuture(Option<Spawn<Box<Future<Item = (), Error = ()>>>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Core {
|
|
||||||
pub fn new(tx: Box<CoreSender>, rx: Box<CoreReceiver>) -> Self {
|
|
||||||
let tx2 = tx.clone_sender();
|
|
||||||
Self {
|
|
||||||
notify: Arc::new(Notifier {
|
|
||||||
tx: Mutex::new(tx2),
|
|
||||||
}),
|
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
next_vacant: Cell::new(0),
|
|
||||||
tasks: RefCell::new(Vec::new()),
|
|
||||||
external_spawn: Arc::new(SpawningFromAnotherThread::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_spawner(&self) -> Spawner {
|
|
||||||
Spawner {
|
|
||||||
tx: self.tx.clone_sender(),
|
|
||||||
external_spawn: Arc::clone(&self.external_spawn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawn a future to be executed by a future call to `turn`.
|
|
||||||
/// The future `f` provided will not be executed until the
|
|
||||||
/// `turn` method is called.
|
|
||||||
pub fn spawn<F>(&self, f: F)
|
|
||||||
where
|
|
||||||
F: Future<Item = (), Error = ()> + 'static,
|
|
||||||
{
|
|
||||||
self.spawn_impl(Box::new(f), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_impl(&self, f: Box<Future<Item = (), Error = ()> + 'static>, do_tx: bool) -> usize {
|
|
||||||
let idx = self.next_vacant.get();
|
|
||||||
let mut tasks = self.tasks.borrow_mut();
|
|
||||||
match tasks.get_mut(idx) {
|
|
||||||
Some(&mut Slot::Vacant { next_vacant }) => {
|
|
||||||
self.next_vacant.set(next_vacant);
|
|
||||||
}
|
|
||||||
Some(&mut Slot::RunningFuture(_)) => panic!("vacant points to running future"),
|
|
||||||
None => {
|
|
||||||
assert_eq!(idx, tasks.len());
|
|
||||||
tasks.push(Slot::Vacant { next_vacant: 0 });
|
|
||||||
self.next_vacant.set(idx + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks[idx] = Slot::RunningFuture(Some(executor::spawn(Box::new(f))));
|
|
||||||
if do_tx {
|
|
||||||
self.tx.send(idx).unwrap();
|
|
||||||
}
|
|
||||||
idx
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_external(&self) -> Option<usize> {
|
|
||||||
let mut futures = self.external_spawn.futures.lock().unwrap();
|
|
||||||
if let Some(future) = futures.pop_front() {
|
|
||||||
Some(self.spawn_impl(future, false))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "Turns" this event loop one tick.
|
|
||||||
/// Does not block.
|
|
||||||
/// Returns `false` if there were no futures in a known-ready state.
|
|
||||||
pub fn turn(&self) -> bool {
|
|
||||||
let task_id = match self.rx.try_recv() {
|
|
||||||
Ok(task_id) if task_id == EXTERNAL_SPAWN => match self.spawn_external() {
|
|
||||||
Some(task_id) => task_id,
|
|
||||||
_ => return false,
|
|
||||||
},
|
|
||||||
Ok(task_id) => task_id,
|
|
||||||
Err(mpsc::TryRecvError::Empty) => return false,
|
|
||||||
Err(mpsc::TryRecvError::Disconnected) => panic!("futurecore rx Disconnected"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// This may be a spurious wakeup so we're not guaranteed to have a
|
|
||||||
// future associated with `task_id`, so do a fallible lookup.
|
|
||||||
//
|
|
||||||
// Note that we don't want to borrow `self.tasks` for too long so we
|
|
||||||
// try to extract the future here and leave behind a tombstone future
|
|
||||||
// which'll get replaced or removed later. This is how we support
|
|
||||||
// spawn-in-run.
|
|
||||||
let mut future = match self.tasks.borrow_mut().get_mut(task_id) {
|
|
||||||
Some(&mut Slot::RunningFuture(ref mut future)) => future.take().unwrap(),
|
|
||||||
Some(&mut Slot::Vacant { .. }) | None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Drive this future forward. If it's done we remove it and if it's not
|
|
||||||
// done then we put it back in the tasks array.
|
|
||||||
let done = match future.poll_future_notify(&self.notify, task_id) {
|
|
||||||
Ok(Async::Ready(())) | Err(()) => true,
|
|
||||||
Ok(Async::NotReady) => false,
|
|
||||||
};
|
|
||||||
let mut tasks = self.tasks.borrow_mut();
|
|
||||||
if done {
|
|
||||||
tasks[task_id] = Slot::Vacant {
|
|
||||||
next_vacant: self.next_vacant.get(),
|
|
||||||
};
|
|
||||||
self.next_vacant.set(task_id);
|
|
||||||
} else {
|
|
||||||
tasks[task_id] = Slot::RunningFuture(Some(future));
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> Executor<F> for Core
|
|
||||||
where
|
|
||||||
F: Future<Item = (), Error = ()> + 'static,
|
|
||||||
{
|
|
||||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
|
||||||
self.spawn(future);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Notifier {
|
|
||||||
tx: Mutex<Box<CoreSender>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Notify for Notifier {
|
|
||||||
fn notify(&self, id: usize) {
|
|
||||||
self.tx.lock().unwrap().send(id).ok();
|
|
||||||
}
|
|
||||||
}
|
|
@ -30,7 +30,6 @@ struct Host {
|
|||||||
/// fullscreen mode.
|
/// fullscreen mode.
|
||||||
is_fullscreen: Option<LogicalPosition>,
|
is_fullscreen: Option<LogicalPosition>,
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
fonts: Rc<FontConfiguration>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HostHelper for Host {
|
impl HostHelper for Host {
|
||||||
@ -235,7 +234,6 @@ impl GliumTerminalWindow {
|
|||||||
window_position,
|
window_position,
|
||||||
is_fullscreen: None,
|
is_fullscreen: None,
|
||||||
config: Arc::clone(config),
|
config: Arc::clone(config),
|
||||||
fonts: Rc::clone(fonts),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
host.display.gl_window().set_cursor(MouseCursor::Text);
|
host.display.gl_window().set_cursor(MouseCursor::Text);
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
use super::GuiSystem;
|
use super::GuiSystem;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::font::{FontConfiguration, FontSystemSelection};
|
use crate::font::{FontConfiguration, FontSystemSelection};
|
||||||
use crate::futurecore;
|
|
||||||
use crate::gliumwindows;
|
use crate::gliumwindows;
|
||||||
pub use crate::gliumwindows::GliumTerminalWindow;
|
pub use crate::gliumwindows::GliumTerminalWindow;
|
||||||
use crate::guicommon::tabs::Tab;
|
use crate::guicommon::tabs::Tab;
|
||||||
use crate::guicommon::window::TerminalWindow;
|
use crate::guicommon::window::TerminalWindow;
|
||||||
use crate::guiloop::SessionTerminated;
|
use crate::guiloop::SessionTerminated;
|
||||||
use crate::mux::{Mux, PtyEvent, PtyEventSender};
|
use crate::mux::Mux;
|
||||||
use crate::spawn_tab;
|
use crate::spawn_tab;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::future;
|
|
||||||
use glium;
|
use glium;
|
||||||
use glium::glutin::EventsLoopProxy;
|
use glium::glutin::EventsLoopProxy;
|
||||||
pub use glium::glutin::WindowId;
|
pub use glium::glutin::WindowId;
|
||||||
@ -40,27 +38,6 @@ impl<T: Send> GuiSender<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl futurecore::CoreSender for GuiSender<usize> {
|
|
||||||
fn send(&self, idx: usize) -> Result<(), Error> {
|
|
||||||
GuiSender::send(self, idx)
|
|
||||||
}
|
|
||||||
fn clone_sender(&self) -> Box<futurecore::CoreSender> {
|
|
||||||
Box::new(GuiSender::clone(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PtyEventSender for GuiSender<PtyEvent> {
|
|
||||||
fn send(&self, event: PtyEvent) -> Result<(), Error> {
|
|
||||||
GuiSender::send(self, event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl futurecore::CoreReceiver for Receiver<usize> {
|
|
||||||
fn try_recv(&self) -> Result<usize, mpsc::TryRecvError> {
|
|
||||||
Receiver::try_recv(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn channel<T: Send>(proxy: EventsLoopProxy) -> (GuiSender<T>, Receiver<T>) {
|
pub fn channel<T: Send>(proxy: EventsLoopProxy) -> (GuiSender<T>, Receiver<T>) {
|
||||||
// Set an upper bound on the number of items in the queue, so that
|
// Set an upper bound on the number of items in the queue, so that
|
||||||
// we don't swamp the gui loop; this puts back pressure on the
|
// we don't swamp the gui loop; this puts back pressure on the
|
||||||
@ -92,9 +69,6 @@ struct Windows {
|
|||||||
pub struct GuiEventLoop {
|
pub struct GuiEventLoop {
|
||||||
pub event_loop: RefCell<glium::glutin::EventsLoop>,
|
pub event_loop: RefCell<glium::glutin::EventsLoop>,
|
||||||
windows: Rc<RefCell<Windows>>,
|
windows: Rc<RefCell<Windows>>,
|
||||||
core: futurecore::Core,
|
|
||||||
poll_tx: GuiSender<PtyEvent>,
|
|
||||||
poll_rx: Receiver<PtyEvent>,
|
|
||||||
gui_tx: Arc<GuiSender<SpawnFunc>>,
|
gui_tx: Arc<GuiSender<SpawnFunc>>,
|
||||||
gui_rx: Receiver<SpawnFunc>,
|
gui_rx: Receiver<SpawnFunc>,
|
||||||
tick_rx: Receiver<()>,
|
tick_rx: Receiver<()>,
|
||||||
@ -118,24 +92,9 @@ impl GlutinGuiSystem {
|
|||||||
GLUTIN_EVENT_LOOP.with(|f| *f.borrow_mut() = Some(Rc::clone(&event_loop)));
|
GLUTIN_EVENT_LOOP.with(|f| *f.borrow_mut() = Some(Rc::clone(&event_loop)));
|
||||||
Ok(Rc::new(Self { event_loop }))
|
Ok(Rc::new(Self { event_loop }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loop through the core and dispatch any tasks that have been
|
|
||||||
/// notified as ready to run. Returns once all such tasks have
|
|
||||||
/// been polled and there are no more pending task notifications.
|
|
||||||
fn process_futures(&self) {
|
|
||||||
loop {
|
|
||||||
if !self.event_loop.core.turn() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuiSystem for GlutinGuiSystem {
|
impl GuiSystem for GlutinGuiSystem {
|
||||||
fn pty_sender(&self) -> Box<PtyEventSender> {
|
|
||||||
Box::new(self.event_loop.poll_tx.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gui_executor(&self) -> Arc<Executor + Sync + Send> {
|
fn gui_executor(&self) -> Arc<Executor + Sync + Send> {
|
||||||
self.event_loop.gui_executor()
|
self.event_loop.gui_executor()
|
||||||
}
|
}
|
||||||
@ -145,8 +104,6 @@ impl GuiSystem for GlutinGuiSystem {
|
|||||||
// https://github.com/tomaka/winit/issues/413
|
// https://github.com/tomaka/winit/issues/413
|
||||||
let myself = &self.event_loop;
|
let myself = &self.event_loop;
|
||||||
loop {
|
loop {
|
||||||
self.process_futures();
|
|
||||||
|
|
||||||
// Check the window count; if after processing the futures there
|
// Check the window count; if after processing the futures there
|
||||||
// are no windows left, then we are done.
|
// are no windows left, then we are done.
|
||||||
{
|
{
|
||||||
@ -158,7 +115,6 @@ impl GuiSystem for GlutinGuiSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
myself.run_event_loop()?;
|
myself.run_event_loop()?;
|
||||||
myself.process_poll()?;
|
|
||||||
myself.process_gui_exec()?;
|
myself.process_gui_exec()?;
|
||||||
myself.process_tick()?;
|
myself.process_tick()?;
|
||||||
}
|
}
|
||||||
@ -180,11 +136,6 @@ impl GuiEventLoop {
|
|||||||
pub fn new(mux: &Rc<Mux>) -> Result<Self, Error> {
|
pub fn new(mux: &Rc<Mux>) -> Result<Self, Error> {
|
||||||
let event_loop = glium::glutin::EventsLoop::new();
|
let event_loop = glium::glutin::EventsLoop::new();
|
||||||
|
|
||||||
let (fut_tx, fut_rx) = channel(event_loop.create_proxy());
|
|
||||||
let core = futurecore::Core::new(Box::new(fut_tx), Box::new(fut_rx));
|
|
||||||
mux.set_spawner(core.get_spawner());
|
|
||||||
|
|
||||||
let (poll_tx, poll_rx) = channel(event_loop.create_proxy());
|
|
||||||
let (gui_tx, gui_rx) = channel(event_loop.create_proxy());
|
let (gui_tx, gui_rx) = channel(event_loop.create_proxy());
|
||||||
|
|
||||||
// The glutin/glium plumbing has no native tick/timer stuff, so
|
// The glutin/glium plumbing has no native tick/timer stuff, so
|
||||||
@ -198,9 +149,6 @@ impl GuiEventLoop {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
core,
|
|
||||||
poll_tx,
|
|
||||||
poll_rx,
|
|
||||||
gui_rx,
|
gui_rx,
|
||||||
gui_tx: Arc::new(gui_tx),
|
gui_tx: Arc::new(gui_tx),
|
||||||
tick_rx,
|
tick_rx,
|
||||||
@ -227,8 +175,7 @@ impl GuiEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_tab(&self, tab: &Rc<Tab>) -> Result<(), Error> {
|
pub fn register_tab(&self, tab: &Rc<Tab>) -> Result<(), Error> {
|
||||||
self.mux
|
self.mux.add_tab(self.gui_executor(), tab)
|
||||||
.add_tab(self.gui_executor(), Box::new(self.poll_tx.clone()), tab)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_spawn_new_window(
|
fn do_spawn_new_window(
|
||||||
@ -237,8 +184,7 @@ impl GuiEventLoop {
|
|||||||
fonts: &Rc<FontConfiguration>,
|
fonts: &Rc<FontConfiguration>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let tab = spawn_tab(&config, None)?;
|
let tab = spawn_tab(&config, None)?;
|
||||||
let sender = Box::new(self.poll_tx.clone());
|
self.mux.add_tab(self.gui_executor(), &tab)?;
|
||||||
self.mux.add_tab(self.gui_executor(), sender, &tab)?;
|
|
||||||
let events = Self::get().expect("to be called on gui thread");
|
let events = Self::get().expect("to be called on gui thread");
|
||||||
let window = GliumTerminalWindow::new(&events, &fonts, &config, &tab)?;
|
let window = GliumTerminalWindow::new(&events, &fonts, &config, &tab)?;
|
||||||
|
|
||||||
@ -349,29 +295,6 @@ impl GuiEventLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process events on poll_rx. We may have a pty
|
|
||||||
/// event or our interval timer may have expired, indicating that
|
|
||||||
/// we need to paint.
|
|
||||||
fn process_poll(&self) -> Result<(), Error> {
|
|
||||||
let start = SystemTime::now();
|
|
||||||
loop {
|
|
||||||
match start.elapsed() {
|
|
||||||
Ok(elapsed) if elapsed > MAX_POLL_LOOP_DURATION => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
match self.poll_rx.try_recv() {
|
|
||||||
Ok(event) => self.mux.process_pty_event(event)?,
|
|
||||||
Err(TryRecvError::Empty) => return Ok(()),
|
|
||||||
Err(err) => bail!("poll_rx disconnected {:?}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_gui_exec(&self) -> Result<(), Error> {
|
fn process_gui_exec(&self) -> Result<(), Error> {
|
||||||
let start = SystemTime::now();
|
let start = SystemTime::now();
|
||||||
loop {
|
loop {
|
||||||
|
@ -2,7 +2,7 @@ use super::ExitStatus;
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::font::FontConfiguration;
|
use crate::font::FontConfiguration;
|
||||||
use crate::guicommon::tabs::Tab;
|
use crate::guicommon::tabs::Tab;
|
||||||
use crate::mux::{Mux, PtyEventSender};
|
use crate::mux::Mux;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use promise::Executor;
|
use promise::Executor;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -95,8 +95,6 @@ pub trait GuiSystem {
|
|||||||
tab: &Rc<Tab>,
|
tab: &Rc<Tab>,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
fn pty_sender(&self) -> Box<PtyEventSender>;
|
|
||||||
|
|
||||||
fn gui_executor(&self) -> Arc<Executor + Sync + Send>;
|
fn gui_executor(&self) -> Arc<Executor + Sync + Send>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ impl GuiEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_tab(&self, tab: &Rc<Tab>) -> Result<(), Error> {
|
pub fn register_tab(&self, tab: &Rc<Tab>) -> Result<(), Error> {
|
||||||
self.mux.add_tab(Box::new(self.pty_tx.clone()), tab)
|
self.mux.add_tab(self.gui_executor(), tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self) -> Result<(), Error> {
|
fn run(&self) -> Result<(), Error> {
|
||||||
@ -241,8 +241,7 @@ impl GuiEventLoop {
|
|||||||
fonts: &Rc<FontConfiguration>,
|
fonts: &Rc<FontConfiguration>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let tab = spawn_tab(&config, None)?;
|
let tab = spawn_tab(&config, None)?;
|
||||||
let sender = Box::new(events.pty_tx.clone());
|
events.mux.add_tab(self.gui_executor(), &tab)?;
|
||||||
events.mux.add_tab(sender, &tab)?;
|
|
||||||
let window = X11TerminalWindow::new(&events, &fonts, &config, &tab)?;
|
let window = X11TerminalWindow::new(&events, &fonts, &config, &tab)?;
|
||||||
|
|
||||||
events.add_window(window)
|
events.add_window(window)
|
||||||
|
@ -17,7 +17,6 @@ use std::rc::Rc;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod futurecore;
|
|
||||||
mod gliumwindows;
|
mod gliumwindows;
|
||||||
mod guicommon;
|
mod guicommon;
|
||||||
mod guiloop;
|
mod guiloop;
|
||||||
@ -173,7 +172,7 @@ fn spawn_window(
|
|||||||
fontconfig: &Rc<FontConfiguration>,
|
fontconfig: &Rc<FontConfiguration>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let tab = spawn_tab(config, cmd)?;
|
let tab = spawn_tab(config, cmd)?;
|
||||||
mux.add_tab(gui.gui_executor(), gui.pty_sender(), &tab)?;
|
mux.add_tab(gui.gui_executor(), &tab)?;
|
||||||
|
|
||||||
gui.spawn_new_window(config, &fontconfig, &tab)
|
gui.spawn_new_window(config, &fontconfig, &tab)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::futurecore::Spawner;
|
|
||||||
use crate::guicommon::tabs::{Tab, TabId};
|
use crate::guicommon::tabs::{Tab, TabId};
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use promise::Executor;
|
use promise::{Executor, Future};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@ -16,53 +15,44 @@ pub mod renderable;
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Mux {
|
pub struct Mux {
|
||||||
tabs: RefCell<HashMap<TabId, Rc<Tab>>>,
|
tabs: RefCell<HashMap<TabId, Rc<Tab>>>,
|
||||||
spawner: RefCell<Option<Spawner>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
fn read_from_tab_pty(executor: Arc<Executor>, tab_id: TabId, mut reader: Box<std::io::Read>) {
|
||||||
pub enum PtyEvent {
|
|
||||||
Data { tab_id: TabId, data: Vec<u8> },
|
|
||||||
Terminated { tab_id: TabId },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PtyEventSender: Send {
|
|
||||||
fn send(&self, event: PtyEvent) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_from_tab_pty(
|
|
||||||
executor: Arc<Executor>,
|
|
||||||
spawner: Spawner,
|
|
||||||
sender: Box<PtyEventSender>,
|
|
||||||
tab_id: TabId,
|
|
||||||
mut reader: Box<std::io::Read>,
|
|
||||||
) {
|
|
||||||
const BUFSIZE: usize = 32 * 1024;
|
const BUFSIZE: usize = 32 * 1024;
|
||||||
let mut buf = [0; BUFSIZE];
|
let mut buf = [0; BUFSIZE];
|
||||||
loop {
|
loop {
|
||||||
match reader.read(&mut buf) {
|
match reader.read(&mut buf) {
|
||||||
Ok(size) if size == 0 => {
|
Ok(size) if size == 0 => {
|
||||||
eprintln!("read_pty EOF: tab_id {}", tab_id);
|
eprintln!("read_pty EOF: tab_id {}", tab_id);
|
||||||
sender.send(PtyEvent::Terminated { tab_id }).ok();
|
Future::with_executor(Arc::clone(&executor), move || {
|
||||||
|
let mux = Mux::get().unwrap();
|
||||||
|
mux.remove_tab(tab_id);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(size) => {
|
Ok(size) => {
|
||||||
spawner.spawn(Box::new(futures::future::lazy(|| {
|
let data = buf[0..size].to_vec();
|
||||||
eprintln!("I was spawned from a pty thread");
|
Future::with_executor(Arc::clone(&executor), move || {
|
||||||
futures::future::ok(())
|
let mux = Mux::get().unwrap();
|
||||||
})));
|
if let Some(tab) = mux.get_tab(tab_id) {
|
||||||
if sender
|
tab.advance_bytes(
|
||||||
.send(PtyEvent::Data {
|
&data,
|
||||||
tab_id,
|
&mut Host {
|
||||||
data: buf[0..size].to_vec(),
|
writer: &mut *tab.writer(),
|
||||||
})
|
},
|
||||||
.is_err()
|
);
|
||||||
{
|
}
|
||||||
return;
|
Ok(())
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("read_pty failed: tab {} {:?}", tab_id, err);
|
eprintln!("read_pty failed: tab {} {:?}", tab_id, err);
|
||||||
sender.send(PtyEvent::Terminated { tab_id }).ok();
|
Future::with_executor(Arc::clone(&executor), move || {
|
||||||
|
let mux = Mux::get().unwrap();
|
||||||
|
mux.remove_tab(tab_id);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,10 +96,6 @@ thread_local! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mux {
|
impl Mux {
|
||||||
pub fn set_spawner(&self, spawner: Spawner) {
|
|
||||||
*self.spawner.borrow_mut() = Some(spawner);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_mux(mux: &Rc<Mux>) {
|
pub fn set_mux(mux: &Rc<Mux>) {
|
||||||
MUX.with(|m| {
|
MUX.with(|m| {
|
||||||
*m.borrow_mut() = Some(Rc::clone(mux));
|
*m.borrow_mut() = Some(Rc::clone(mux));
|
||||||
@ -130,18 +116,12 @@ impl Mux {
|
|||||||
self.tabs.borrow().get(&tab_id).map(Rc::clone)
|
self.tabs.borrow().get(&tab_id).map(Rc::clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_tab(
|
pub fn add_tab(&self, executor: Arc<Executor>, tab: &Rc<Tab>) -> Result<(), Error> {
|
||||||
&self,
|
|
||||||
executor: Arc<Executor + Send + Sync>,
|
|
||||||
sender: Box<PtyEventSender>,
|
|
||||||
tab: &Rc<Tab>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.tabs.borrow_mut().insert(tab.tab_id(), Rc::clone(tab));
|
self.tabs.borrow_mut().insert(tab.tab_id(), Rc::clone(tab));
|
||||||
|
|
||||||
let reader = tab.reader()?;
|
let reader = tab.reader()?;
|
||||||
let tab_id = tab.tab_id();
|
let tab_id = tab.tab_id();
|
||||||
let spawner = self.spawner.borrow().as_ref().unwrap().clone();
|
thread::spawn(move || read_from_tab_pty(executor, tab_id, reader));
|
||||||
thread::spawn(move || read_from_tab_pty(executor, spawner, sender, tab_id, reader));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -151,28 +131,6 @@ impl Mux {
|
|||||||
self.tabs.borrow_mut().remove(&tab_id);
|
self.tabs.borrow_mut().remove(&tab_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_pty_event(&self, event: PtyEvent) -> Result<(), Error> {
|
|
||||||
match event {
|
|
||||||
PtyEvent::Data { tab_id, data } => {
|
|
||||||
if let Some(tab) = self.get_tab(tab_id) {
|
|
||||||
tab.advance_bytes(
|
|
||||||
&data,
|
|
||||||
&mut Host {
|
|
||||||
writer: &mut *tab.writer(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtyEvent::Terminated { tab_id } => {
|
|
||||||
// The fact that we woke up is enough to trigger each
|
|
||||||
// window to check for termination
|
|
||||||
eprintln!("tab {} terminated", tab_id);
|
|
||||||
self.remove_tab(tab_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.tabs.borrow().is_empty()
|
self.tabs.borrow().is_empty()
|
||||||
|
Loading…
Reference in New Issue
Block a user