mirror of
https://github.com/wez/wezterm.git
synced 2025-01-08 23:17:36 +03:00
Windows can be labelled with a workspace name
This is not exposed through any UX; the mux api allows setting the workspace and propagating information about windows whose workspace has changed. Windows start with a blank workspace name. This is just plumbing; nothing uses it yet. refs: #1531
This commit is contained in:
parent
f6e56aab10
commit
cefe45d206
@ -406,7 +406,7 @@ macro_rules! pdu {
|
||||
/// The overall version of the codec.
|
||||
/// This must be bumped when backwards incompatible changes
|
||||
/// are made to the types and protocol.
|
||||
pub const CODEC_VERSION: usize = 14;
|
||||
pub const CODEC_VERSION: usize = 15;
|
||||
|
||||
// Defines the Pdu enum.
|
||||
// Each struct has an explicit identifying number.
|
||||
@ -448,6 +448,8 @@ pdu! {
|
||||
SetClientId: 40,
|
||||
GetClientList: 41,
|
||||
GetClientListResponse: 42,
|
||||
SetWindowWorkspace: 43,
|
||||
WindowWorkspaceChanged: 44,
|
||||
}
|
||||
|
||||
impl Pdu {
|
||||
@ -688,6 +690,12 @@ pub struct SetClipboard {
|
||||
pub selection: ClipboardSelection,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct SetWindowWorkspace {
|
||||
pub window_id: WindowId,
|
||||
pub workspace: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct SetPalette {
|
||||
pub pane_id: PaneId,
|
||||
@ -700,6 +708,12 @@ pub struct NotifyAlert {
|
||||
pub alert: Alert,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct WindowWorkspaceChanged {
|
||||
pub window_id: WindowId,
|
||||
pub workspace: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct SetClientId {
|
||||
pub client_id: ClientId,
|
||||
|
@ -48,6 +48,7 @@ pub enum MuxNotification {
|
||||
WindowCreated(WindowId),
|
||||
WindowRemoved(WindowId),
|
||||
WindowInvalidated(WindowId),
|
||||
WindowWorkspaceChanged(WindowId),
|
||||
Alert {
|
||||
pane_id: PaneId,
|
||||
alert: wezterm_term::Alert,
|
||||
@ -627,6 +628,21 @@ impl Mux {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn iter_windows_in_workspace(&self, workspace: &str) -> Vec<WindowId> {
|
||||
self.windows
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(k, w)| {
|
||||
if w.get_workspace() == workspace {
|
||||
Some(k)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn iter_windows(&self) -> Vec<WindowId> {
|
||||
self.windows.borrow().keys().cloned().collect()
|
||||
}
|
||||
|
@ -156,12 +156,17 @@ fn pane_tree(
|
||||
window_id: WindowId,
|
||||
active: Option<&Rc<dyn Pane>>,
|
||||
zoomed: Option<&Rc<dyn Pane>>,
|
||||
workspace: &str,
|
||||
) -> PaneNode {
|
||||
match tree {
|
||||
Tree::Empty => PaneNode::Empty,
|
||||
Tree::Node { left, right, data } => PaneNode::Split {
|
||||
left: Box::new(pane_tree(&*left, tab_id, window_id, active, zoomed)),
|
||||
right: Box::new(pane_tree(&*right, tab_id, window_id, active, zoomed)),
|
||||
left: Box::new(pane_tree(
|
||||
&*left, tab_id, window_id, active, zoomed, workspace,
|
||||
)),
|
||||
right: Box::new(pane_tree(
|
||||
&*right, tab_id, window_id, active, zoomed, workspace,
|
||||
)),
|
||||
node: data.unwrap(),
|
||||
},
|
||||
Tree::Leaf(pane) => {
|
||||
@ -182,6 +187,7 @@ fn pane_tree(
|
||||
pixel_width: 0,
|
||||
},
|
||||
working_dir: working_dir.map(Into::into),
|
||||
workspace: workspace.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -484,10 +490,28 @@ impl Tab {
|
||||
}
|
||||
};
|
||||
|
||||
let workspace = match mux
|
||||
.get_window(window_id)
|
||||
.map(|w| w.get_workspace().to_string())
|
||||
{
|
||||
Some(ws) => ws,
|
||||
None => {
|
||||
log::error!("window id {} doesn't have a window!?", window_id);
|
||||
return PaneNode::Empty;
|
||||
}
|
||||
};
|
||||
|
||||
let zoomed = self.zoomed.borrow();
|
||||
let active = self.get_active_pane();
|
||||
if let Some(root) = self.pane.borrow().as_ref() {
|
||||
pane_tree(root, tab_id, window_id, active.as_ref(), zoomed.as_ref())
|
||||
pane_tree(
|
||||
root,
|
||||
tab_id,
|
||||
window_id,
|
||||
active.as_ref(),
|
||||
zoomed.as_ref(),
|
||||
&workspace,
|
||||
)
|
||||
} else {
|
||||
PaneNode::Empty
|
||||
}
|
||||
@ -1539,6 +1563,7 @@ pub struct PaneEntry {
|
||||
pub working_dir: Option<SerdeUrl>,
|
||||
pub is_active_pane: bool,
|
||||
pub is_zoomed_pane: bool,
|
||||
pub workspace: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Serialize, PartialEq, Debug)]
|
||||
|
@ -13,6 +13,7 @@ pub struct Window {
|
||||
active: usize,
|
||||
last_active: Option<TabId>,
|
||||
clipboard: Option<Arc<dyn Clipboard>>,
|
||||
workspace: String,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
@ -23,6 +24,21 @@ impl Window {
|
||||
active: 0,
|
||||
last_active: None,
|
||||
clipboard: None,
|
||||
workspace: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_workspace(&self) -> &str {
|
||||
&self.workspace
|
||||
}
|
||||
|
||||
pub fn set_workspace(&mut self, workspace: &str) {
|
||||
if workspace == self.workspace {
|
||||
return;
|
||||
}
|
||||
self.workspace = workspace.to_string();
|
||||
if let Some(mux) = Mux::get() {
|
||||
mux.notify(MuxNotification::WindowWorkspaceChanged(self.id));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +179,41 @@ fn process_unilateral(
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
match &decoded.pdu {
|
||||
Pdu::WindowWorkspaceChanged(WindowWorkspaceChanged {
|
||||
window_id,
|
||||
workspace,
|
||||
}) => {
|
||||
let window_id = *window_id;
|
||||
let workspace = workspace.to_string();
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
let mux = Mux::get().ok_or_else(|| anyhow!("no more mux"))?;
|
||||
let client_domain = mux
|
||||
.get_domain(local_domain_id)
|
||||
.ok_or_else(|| anyhow!("no such domain {}", local_domain_id))?;
|
||||
let client_domain =
|
||||
client_domain
|
||||
.downcast_ref::<ClientDomain>()
|
||||
.ok_or_else(|| {
|
||||
anyhow!("domain {} is not a ClientDomain instance", local_domain_id)
|
||||
})?;
|
||||
|
||||
let local_window_id = client_domain
|
||||
.remote_to_local_window_id(window_id)
|
||||
.ok_or_else(|| anyhow!("no local window for remote window id {}", window_id))?;
|
||||
if let Some(mut window) = mux.get_window_mut(local_window_id) {
|
||||
window.set_workspace(&workspace);
|
||||
}
|
||||
|
||||
anyhow::Result::<()>::Ok(())
|
||||
})
|
||||
.detach();
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(pane_id) = decoded.pdu.pane_id() {
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
process_unilateral_inner(pane_id, local_domain_id, decoded)
|
||||
@ -1127,4 +1162,5 @@ impl Client {
|
||||
rpc!(kill_pane, KillPane, UnitResponse);
|
||||
rpc!(set_client_id, SetClientId, UnitResponse);
|
||||
rpc!(list_clients, GetClientList, GetClientListResponse);
|
||||
rpc!(set_window_workspace, SetWindowWorkspace, UnitResponse);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use mux::domain::{alloc_domain_id, Domain, DomainId, DomainState};
|
||||
use mux::pane::{Pane, PaneId};
|
||||
use mux::tab::{SplitDirection, Tab, TabId};
|
||||
use mux::window::WindowId;
|
||||
use mux::Mux;
|
||||
use mux::{Mux, MuxNotification};
|
||||
use portable_pty::{CommandBuilder, PtySize};
|
||||
use promise::spawn::spawn_into_new_thread;
|
||||
use std::cell::RefCell;
|
||||
@ -176,10 +176,55 @@ pub struct ClientDomain {
|
||||
local_domain_id: DomainId,
|
||||
}
|
||||
|
||||
async fn update_remote_workspace(
|
||||
local_domain_id: DomainId,
|
||||
pdu: codec::SetWindowWorkspace,
|
||||
) -> anyhow::Result<()> {
|
||||
let inner = ClientDomain::get_client_inner_for_domain(local_domain_id)?;
|
||||
inner.client.set_window_workspace(pdu).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mux_notify_client_domain(local_domain_id: DomainId, notif: MuxNotification) -> bool {
|
||||
let mux = Mux::get().expect("called by mux");
|
||||
let domain = match mux.get_domain(local_domain_id) {
|
||||
Some(domain) => domain,
|
||||
None => return false,
|
||||
};
|
||||
let domain = match domain.downcast_ref::<ClientDomain>() {
|
||||
Some(domain) => domain,
|
||||
None => return false,
|
||||
};
|
||||
match notif {
|
||||
MuxNotification::WindowWorkspaceChanged(window_id) => {
|
||||
if let Some(remote_window_id) = domain.local_to_remote_window_id(window_id) {
|
||||
if let Some(workspace) = mux
|
||||
.get_window(window_id)
|
||||
.map(|w| w.get_workspace().to_string())
|
||||
{
|
||||
let request = codec::SetWindowWorkspace {
|
||||
window_id: remote_window_id,
|
||||
workspace,
|
||||
};
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
let _ = update_remote_workspace(local_domain_id, request).await;
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
impl ClientDomain {
|
||||
pub fn new(config: ClientDomainConfig) -> Self {
|
||||
let local_domain_id = alloc_domain_id();
|
||||
let label = config.label();
|
||||
Mux::get()
|
||||
.expect("created on main thread")
|
||||
.subscribe(move |notif| mux_notify_client_domain(local_domain_id, notif));
|
||||
Self {
|
||||
config,
|
||||
label,
|
||||
@ -208,6 +253,16 @@ impl ClientDomain {
|
||||
inner.remote_to_local_pane_id(remote_pane_id)
|
||||
}
|
||||
|
||||
pub fn remote_to_local_window_id(&self, remote_window_id: WindowId) -> Option<WindowId> {
|
||||
let inner = self.inner()?;
|
||||
inner.remote_to_local_window(remote_window_id)
|
||||
}
|
||||
|
||||
pub fn local_to_remote_window_id(&self, local_window_id: WindowId) -> Option<WindowId> {
|
||||
let inner = self.inner()?;
|
||||
inner.local_to_remote_window(local_window_id)
|
||||
}
|
||||
|
||||
pub fn get_client_inner_for_domain(domain_id: DomainId) -> anyhow::Result<Arc<ClientInner>> {
|
||||
let mux = Mux::get().unwrap();
|
||||
let domain = mux
|
||||
|
@ -38,6 +38,7 @@ impl GuiFrontEnd {
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
MuxNotification::WindowWorkspaceChanged(_) => {}
|
||||
MuxNotification::WindowRemoved(_) => {}
|
||||
MuxNotification::PaneRemoved(_) => {}
|
||||
MuxNotification::WindowInvalidated(_) => {}
|
||||
|
@ -99,6 +99,22 @@ where
|
||||
Ok(Item::Notif(MuxNotification::WindowRemoved(_window_id))) => {}
|
||||
Ok(Item::Notif(MuxNotification::WindowCreated(_window_id))) => {}
|
||||
Ok(Item::Notif(MuxNotification::WindowInvalidated(_window_id))) => {}
|
||||
Ok(Item::Notif(MuxNotification::WindowWorkspaceChanged(window_id))) => {
|
||||
let workspace = {
|
||||
let mux = Mux::get().expect("to be running on gui thread");
|
||||
mux.get_window(window_id)
|
||||
.map(|w| w.get_workspace().to_string())
|
||||
};
|
||||
if let Some(workspace) = workspace {
|
||||
Pdu::WindowWorkspaceChanged(codec::WindowWorkspaceChanged {
|
||||
window_id,
|
||||
workspace,
|
||||
})
|
||||
.encode_async(&mut stream, 0)
|
||||
.await?;
|
||||
stream.flush().await.context("flushing PDU to client")?;
|
||||
}
|
||||
}
|
||||
Ok(Item::Notif(MuxNotification::Empty)) => {}
|
||||
Err(err) => {
|
||||
log::error!("process_async Err {}", err);
|
||||
|
@ -281,6 +281,25 @@ impl SessionHandler {
|
||||
|
||||
match decoded.pdu {
|
||||
Pdu::Ping(Ping {}) => send_response(Ok(Pdu::Pong(Pong {}))),
|
||||
Pdu::SetWindowWorkspace(SetWindowWorkspace {
|
||||
window_id,
|
||||
workspace,
|
||||
}) => {
|
||||
spawn_into_main_thread(async move {
|
||||
catch(
|
||||
move || {
|
||||
let mux = Mux::get().unwrap();
|
||||
let mut window = mux
|
||||
.get_window_mut(window_id)
|
||||
.ok_or_else(|| anyhow!("window {} is invalid", window_id))?;
|
||||
window.set_workspace(&workspace);
|
||||
Ok(Pdu::UnitResponse(UnitResponse {}))
|
||||
},
|
||||
send_response,
|
||||
)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
Pdu::SetClientId(SetClientId { client_id }) => {
|
||||
self.client_id.replace(client_id.clone());
|
||||
spawn_into_main_thread(async move {
|
||||
@ -632,6 +651,7 @@ impl SessionHandler {
|
||||
| Pdu::SearchScrollbackResponse { .. }
|
||||
| Pdu::GetLinesResponse { .. }
|
||||
| Pdu::GetCodecVersionResponse { .. }
|
||||
| Pdu::WindowWorkspaceChanged { .. }
|
||||
| Pdu::GetTlsCredsResponse { .. }
|
||||
| Pdu::GetClientListResponse { .. }
|
||||
| Pdu::PaneRemoved { .. }
|
||||
|
@ -472,6 +472,10 @@ async fn run_cli_async(config: config::ConfigHandle, cli: CliCommand) -> anyhow:
|
||||
name: "PANEID".to_string(),
|
||||
alignment: Alignment::Right,
|
||||
},
|
||||
Column {
|
||||
name: "WORKSPACE".to_string(),
|
||||
alignment: Alignment::Left,
|
||||
},
|
||||
Column {
|
||||
name: "SIZE".to_string(),
|
||||
alignment: Alignment::Left,
|
||||
@ -497,6 +501,7 @@ async fn run_cli_async(config: config::ConfigHandle, cli: CliCommand) -> anyhow:
|
||||
entry.window_id.to_string(),
|
||||
entry.tab_id.to_string(),
|
||||
entry.pane_id.to_string(),
|
||||
entry.workspace.to_string(),
|
||||
format!("{}x{}", entry.size.cols, entry.size.rows),
|
||||
entry.title.clone(),
|
||||
entry
|
||||
|
Loading…
Reference in New Issue
Block a user