mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 23:21:08 +03:00
basically working attach to remote tabs/windows
The Domain::spawn interface needs to be adjusted to allow indicating which window we want to spawn into, or whether the spawn should create a new window, but aside from that, we can now attach to a mux server and instantiate tabs and windows!
This commit is contained in:
parent
9b6800f589
commit
7456c06028
@ -4,6 +4,7 @@ use crate::frontend::glium::window::GliumTerminalWindow;
|
||||
use crate::frontend::guicommon::window::TerminalWindow;
|
||||
use crate::frontend::{front_end, FrontEnd};
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::mux::window::WindowId as MuxWindowId;
|
||||
use crate::mux::{Mux, SessionTerminated};
|
||||
use failure::{bail, Error};
|
||||
use glium;
|
||||
@ -145,10 +146,12 @@ impl FrontEnd for GlutinFrontEnd {
|
||||
config: &Arc<Config>,
|
||||
fontconfig: &Rc<FontConfiguration>,
|
||||
tab: &Rc<dyn Tab>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<MuxWindowId, Error> {
|
||||
let window = GliumTerminalWindow::new(&self.event_loop, fontconfig, config, tab)?;
|
||||
|
||||
self.event_loop.add_window(window)
|
||||
let id = window.get_mux_window_id();
|
||||
self.event_loop.add_window(window)?;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::config::Config;
|
||||
use crate::font::FontConfiguration;
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::mux::window::WindowId;
|
||||
use crate::mux::Mux;
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use failure::{format_err, Error};
|
||||
@ -113,7 +114,7 @@ pub trait FrontEnd: Downcast {
|
||||
config: &Arc<Config>,
|
||||
fontconfig: &Rc<FontConfiguration>,
|
||||
tab: &Rc<dyn Tab>,
|
||||
) -> Result<(), Error>;
|
||||
) -> Result<WindowId, Error>;
|
||||
|
||||
fn gui_executor(&self) -> Box<dyn Executor>;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use crate::config::Config;
|
||||
use crate::font::FontConfiguration;
|
||||
use crate::frontend::FrontEnd;
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::mux::window::WindowId;
|
||||
use crate::mux::Mux;
|
||||
use crate::server::listener::spawn_listener;
|
||||
use failure::{bail, Error};
|
||||
@ -79,9 +80,8 @@ impl FrontEnd for MuxServerFrontEnd {
|
||||
&self,
|
||||
_config: &Arc<Config>,
|
||||
_fontconfig: &Rc<FontConfiguration>,
|
||||
_tab: &Rc<dyn Tab>,
|
||||
) -> Result<(), Error> {
|
||||
// The tab was already added to the mux, so we are a NOP
|
||||
Ok(())
|
||||
tab: &Rc<dyn Tab>,
|
||||
) -> Result<WindowId, Error> {
|
||||
Mux::get().unwrap().add_new_window_with_tab(tab)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::frontend::xwindows::xwin::X11TerminalWindow;
|
||||
use crate::frontend::xwindows::Connection;
|
||||
use crate::frontend::FrontEnd;
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::mux::window::WindowId as MuxWindowId;
|
||||
use crate::mux::Mux;
|
||||
use failure::{bail, Error};
|
||||
use log::debug;
|
||||
@ -83,10 +84,12 @@ impl FrontEnd for X11FrontEnd {
|
||||
config: &Arc<Config>,
|
||||
fontconfig: &Rc<FontConfiguration>,
|
||||
tab: &Rc<dyn Tab>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<MuxWindowId, Error> {
|
||||
let window = X11TerminalWindow::new(&self.event_loop, fontconfig, config, tab)?;
|
||||
|
||||
self.event_loop.add_window(window)
|
||||
let id = window.get_mux_window_id();
|
||||
self.event_loop.add_window(window)?;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
30
src/main.rs
30
src/main.rs
@ -14,7 +14,7 @@ mod frontend;
|
||||
mod mux;
|
||||
mod opengl;
|
||||
mod server;
|
||||
use crate::frontend::{FrontEnd, FrontEndSelection};
|
||||
use crate::frontend::FrontEndSelection;
|
||||
use crate::mux::domain::{Domain, LocalDomain};
|
||||
use crate::mux::Mux;
|
||||
use portable_pty::cmdbuilder::CommandBuilder;
|
||||
@ -145,7 +145,11 @@ fn run_terminal_gui(config: Arc<config::Config>, opts: &StartCommand) -> Result<
|
||||
let front_end = opts.front_end.unwrap_or(config.front_end);
|
||||
let gui = front_end.try_new(&mux)?;
|
||||
|
||||
spawn_window(&mux, &*gui, cmd, &fontconfig)?;
|
||||
domain.attach()?;
|
||||
|
||||
let tab = mux.default_domain().spawn(PtySize::default(), cmd)?;
|
||||
gui.spawn_new_window(mux.config(), &fontconfig, &tab)?;
|
||||
|
||||
gui.run_forever()
|
||||
}
|
||||
|
||||
@ -190,33 +194,19 @@ fn main() -> Result<(), Error> {
|
||||
let mut client = Client::new(&config)?;
|
||||
info!("ping: {:?}", client.ping()?);
|
||||
let tabs = client.list_tabs()?;
|
||||
for (tab_id, title) in tabs.tabs.iter() {
|
||||
info!("tab {}: {}", tab_id, title);
|
||||
let _data = client.get_coarse_tab_renderable_data(GetCoarseTabRenderableData {
|
||||
tab_id: *tab_id,
|
||||
dirty_all: true,
|
||||
})?;
|
||||
// info!("coarse: {:?}", data);
|
||||
for entry in tabs.tabs.iter() {
|
||||
info!("tab {} {}: {}", entry.window_id, entry.tab_id, entry.title);
|
||||
}
|
||||
error!(
|
||||
"spawn: {:?}",
|
||||
client.spawn(Spawn {
|
||||
domain_id: 0,
|
||||
size: PtySize::default(),
|
||||
command: None
|
||||
command: None,
|
||||
window_id: None,
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_window(
|
||||
mux: &Rc<Mux>,
|
||||
gui: &dyn FrontEnd,
|
||||
cmd: Option<CommandBuilder>,
|
||||
fontconfig: &Rc<FontConfiguration>,
|
||||
) -> Result<(), Error> {
|
||||
let tab = mux.default_domain().spawn(PtySize::default(), cmd)?;
|
||||
gui.spawn_new_window(mux.config(), &fontconfig, &tab)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use crate::frontend::guicommon::localtab::LocalTab;
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::mux::Mux;
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use failure::Error;
|
||||
use failure::{Error, Fallible};
|
||||
use log::info;
|
||||
use portable_pty::cmdbuilder::CommandBuilder;
|
||||
use portable_pty::{PtySize, PtySystem};
|
||||
@ -31,6 +31,9 @@ pub trait Domain: Downcast {
|
||||
/// Returns the domain id, which is useful for obtaining
|
||||
/// a handle on the domain later.
|
||||
fn domain_id(&self) -> DomainId;
|
||||
|
||||
/// Re-attach to any tabs that might be pre-existing in this domain
|
||||
fn attach(&self) -> Fallible<()>;
|
||||
}
|
||||
impl_downcast!(Domain);
|
||||
|
||||
@ -80,4 +83,8 @@ impl Domain for LocalDomain {
|
||||
fn domain_id(&self) -> DomainId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn attach(&self) -> Fallible<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -171,6 +171,18 @@ impl Mux {
|
||||
pub fn remove_tab(&self, tab_id: TabId) {
|
||||
debug!("removing tab {}", tab_id);
|
||||
self.tabs.borrow_mut().remove(&tab_id);
|
||||
let mut windows = self.windows.borrow_mut();
|
||||
let mut dead_windows = vec![];
|
||||
for (window_id, win) in windows.iter_mut() {
|
||||
if win.remove_by_id(tab_id) && win.is_empty() {
|
||||
dead_windows.push(*window_id);
|
||||
}
|
||||
}
|
||||
|
||||
for window_id in dead_windows {
|
||||
debug!("removing window {}", window_id);
|
||||
windows.remove(&window_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_window(&self, window_id: WindowId) -> Option<Ref<Window>> {
|
||||
@ -208,6 +220,7 @@ impl Mux {
|
||||
self.tabs.borrow().is_empty()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn iter_tabs(&self) -> Vec<Rc<dyn Tab>> {
|
||||
self.tabs
|
||||
.borrow()
|
||||
@ -215,6 +228,10 @@ impl Mux {
|
||||
.map(|(_, v)| Rc::clone(v))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn iter_windows(&self) -> Vec<WindowId> {
|
||||
self.windows.borrow().keys().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
|
@ -48,13 +48,16 @@ impl Window {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_by_id(&mut self, id: TabId) {
|
||||
pub fn remove_by_id(&mut self, id: TabId) -> bool {
|
||||
if let Some(idx) = self.idx_by_id(id) {
|
||||
self.tabs.remove(idx);
|
||||
let len = self.tabs.len();
|
||||
if len > 0 && self.active == idx && idx >= len {
|
||||
self.set_active(len - 1);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,12 @@
|
||||
|
||||
use crate::mux::domain::DomainId;
|
||||
use crate::mux::tab::TabId;
|
||||
use crate::mux::window::WindowId;
|
||||
use failure::{bail, Error};
|
||||
use leb128;
|
||||
use log::debug;
|
||||
use portable_pty::{CommandBuilder, PtySize};
|
||||
use serde_derive::*;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use term::{CursorPosition, Line};
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
@ -242,9 +242,16 @@ pub struct Pong {}
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct ListTabs {}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct WindowAndTabEntry {
|
||||
pub window_id: WindowId,
|
||||
pub tab_id: TabId,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct ListTabsResponse {
|
||||
pub tabs: HashMap<TabId, String>,
|
||||
pub tabs: Vec<WindowAndTabEntry>,
|
||||
}
|
||||
|
||||
/// This is a transitional request to get some basic
|
||||
@ -277,6 +284,8 @@ pub struct GetCoarseTabRenderableDataResponse {
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct Spawn {
|
||||
pub domain_id: DomainId,
|
||||
/// If None, create a new window for this new tab
|
||||
pub window_id: Option<WindowId>,
|
||||
pub command: Option<CommandBuilder>,
|
||||
pub size: PtySize,
|
||||
}
|
||||
@ -284,6 +293,7 @@ pub struct Spawn {
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct SpawnResponse {
|
||||
pub tab_id: TabId,
|
||||
pub window_id: WindowId,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::font::{FontConfiguration, FontSystemSelection};
|
||||
use crate::frontend::front_end;
|
||||
use crate::mux::domain::{alloc_domain_id, Domain, DomainId};
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::mux::Mux;
|
||||
@ -6,6 +8,7 @@ use crate::server::codec::Spawn;
|
||||
use crate::server::tab::ClientTab;
|
||||
use failure::Fallible;
|
||||
use portable_pty::{CommandBuilder, PtySize};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@ -52,6 +55,7 @@ impl Domain for ClientDomain {
|
||||
client
|
||||
.spawn(Spawn {
|
||||
domain_id: self.inner.remote_domain_id,
|
||||
window_id: None,
|
||||
size,
|
||||
command,
|
||||
})?
|
||||
@ -61,4 +65,38 @@ impl Domain for ClientDomain {
|
||||
Mux::get().unwrap().add_tab(&tab)?;
|
||||
Ok(tab)
|
||||
}
|
||||
|
||||
fn attach(&self) -> Fallible<()> {
|
||||
let mux = Mux::get().unwrap();
|
||||
let mut client = self.inner.client.lock().unwrap();
|
||||
let tabs = client.list_tabs()?;
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
|
||||
for entry in tabs.tabs.iter() {
|
||||
log::error!("attaching to remote tab {} {}", entry.tab_id, entry.title);
|
||||
let tab: Rc<dyn Tab> = Rc::new(ClientTab::new(&self.inner, entry.tab_id));
|
||||
mux.add_tab(&tab)?;
|
||||
|
||||
windows
|
||||
.entry(entry.window_id)
|
||||
.and_modify(|local_window_id| {
|
||||
let mut window = mux
|
||||
.get_window_mut(*local_window_id)
|
||||
.expect("no such window!?");
|
||||
window.push(&tab);
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
let fonts = Rc::new(FontConfiguration::new(
|
||||
Arc::clone(mux.config()),
|
||||
FontSystemSelection::get_default(),
|
||||
));
|
||||
front_end()
|
||||
.unwrap()
|
||||
.spawn_new_window(mux.config(), &fonts, &tab)
|
||||
.unwrap()
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use failure::{bail, err_msg, format_err, Error, Fallible};
|
||||
use libc::{mode_t, umask};
|
||||
use log::{debug, error, warn};
|
||||
use promise::{Executor, Future};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{remove_file, DirBuilder};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
|
||||
@ -95,11 +94,30 @@ impl ClientSession {
|
||||
Pdu::Ping(Ping {}) => Pdu::Pong(Pong {}),
|
||||
Pdu::ListTabs(ListTabs {}) => {
|
||||
let result = Future::with_executor(self.executor.clone_executor(), move || {
|
||||
let mut tabs = HashMap::new();
|
||||
let mux = Mux::get().unwrap();
|
||||
for tab in mux.iter_tabs() {
|
||||
tabs.insert(tab.tab_id(), tab.get_title());
|
||||
}
|
||||
let tabs = mux
|
||||
.iter_windows()
|
||||
.into_iter()
|
||||
.flat_map(|window_id| {
|
||||
let window = mux.get_window(window_id).unwrap();
|
||||
let tabs: Vec<WindowAndTabEntry> = window
|
||||
.iter()
|
||||
.map(|tab| WindowAndTabEntry {
|
||||
window_id,
|
||||
tab_id: tab.tab_id(),
|
||||
title: tab.get_title(),
|
||||
})
|
||||
.collect();
|
||||
log::error!(
|
||||
"ListTabs: window {} has {} tabs {:?}",
|
||||
window_id,
|
||||
tabs.len(),
|
||||
tabs
|
||||
);
|
||||
|
||||
tabs.into_iter()
|
||||
})
|
||||
.collect();
|
||||
Ok(ListTabsResponse { tabs })
|
||||
})
|
||||
.wait()?;
|
||||
@ -227,8 +245,20 @@ impl ClientSession {
|
||||
let domain = mux.get_domain(spawn.domain_id).ok_or_else(|| {
|
||||
format_err!("domain {} not found on this server", spawn.domain_id)
|
||||
})?;
|
||||
let tab = domain.spawn(spawn.size, spawn.command)?;
|
||||
let window_id = if let Some(window_id) = spawn.window_id {
|
||||
mux.get_window_mut(window_id)
|
||||
.ok_or_else(|| {
|
||||
format_err!("window_id {} not found on this server", window_id)
|
||||
})?
|
||||
.push(&tab);
|
||||
window_id
|
||||
} else {
|
||||
mux.add_new_window_with_tab(&tab)?
|
||||
};
|
||||
Ok(SpawnResponse {
|
||||
tab_id: domain.spawn(spawn.size, spawn.command)?.tab_id(),
|
||||
tab_id: tab.tab_id(),
|
||||
window_id,
|
||||
})
|
||||
})
|
||||
.wait()?;
|
||||
|
Loading…
Reference in New Issue
Block a user