mirror of
https://github.com/wez/wezterm.git
synced 2024-12-27 15:37:29 +03:00
macos: allow running when there are no windows
Most of this commit is refactoring the spawn logic so that we can reuse most of it to handle spawn requests when there is no GUI window.
This commit is contained in:
parent
f7eb13dd8d
commit
4b3660d166
@ -946,15 +946,16 @@ impl Mux {
|
||||
) -> anyhow::Result<Arc<dyn Domain>> {
|
||||
let domain = match domain {
|
||||
SpawnTabDomain::DefaultDomain => self.default_domain(),
|
||||
SpawnTabDomain::CurrentPaneDomain => {
|
||||
let pane_id = pane_id
|
||||
.ok_or_else(|| anyhow!("CurrentPaneDomain used with no current pane"))?;
|
||||
let (pane_domain_id, _window_id, _tab_id) = self
|
||||
.resolve_pane_id(pane_id)
|
||||
.ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?;
|
||||
self.get_domain(pane_domain_id)
|
||||
.expect("resolve_pane_id to give valid domain_id")
|
||||
}
|
||||
SpawnTabDomain::CurrentPaneDomain => match pane_id {
|
||||
Some(pane_id) => {
|
||||
let (pane_domain_id, _window_id, _tab_id) = self
|
||||
.resolve_pane_id(pane_id)
|
||||
.ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?;
|
||||
self.get_domain(pane_domain_id)
|
||||
.expect("resolve_pane_id to give valid domain_id")
|
||||
}
|
||||
None => self.default_domain(),
|
||||
},
|
||||
SpawnTabDomain::DomainId(domain_id) => self
|
||||
.get_domain(*domain_id)
|
||||
.ok_or_else(|| anyhow!("domain id {} is invalid", domain_id))?,
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::scripting::guiwin::GuiWin;
|
||||
use crate::spawn::SpawnWhere;
|
||||
use crate::termwindow::TermWindowNotif;
|
||||
use crate::TermWindow;
|
||||
use ::window::*;
|
||||
use anyhow::{Context, Error};
|
||||
use config::keyassignment::{KeyAssignment, SpawnCommand};
|
||||
pub use config::FrontEndSelection;
|
||||
use mux::client::ClientId;
|
||||
use mux::window::WindowId as MuxWindowId;
|
||||
@ -98,13 +100,16 @@ impl GuiFrontEnd {
|
||||
| Alert::SetUserVar { .. },
|
||||
} => {}
|
||||
MuxNotification::Empty => {
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
if mux::activity::Activity::count() == 0 {
|
||||
log::trace!("Mux is now empty, terminate gui");
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
if mux::activity::Activity::count() == 0 {
|
||||
log::trace!("Mux is now empty, terminate gui");
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
MuxNotification::SaveToDownloads { name, data } => {
|
||||
if !config::configuration().allow_download_protocols {
|
||||
@ -211,7 +216,45 @@ impl GuiFrontEnd {
|
||||
// and the user picks an action from the menubar.
|
||||
// This is not currently possible, but could be in the
|
||||
// future.
|
||||
log::info!("perform: {action:?}");
|
||||
|
||||
fn spawn_command(spawn: &SpawnCommand, spawn_where: SpawnWhere) {
|
||||
let config = config::configuration();
|
||||
let dpi = config.dpi.unwrap_or_else(|| ::window::default_dpi()) as u32;
|
||||
let size = config.initial_size(dpi);
|
||||
let term_config = Arc::new(config::TermConfig::with_config(config));
|
||||
|
||||
crate::spawn::spawn_command_impl(spawn, spawn_where, size, None, term_config)
|
||||
}
|
||||
|
||||
match action {
|
||||
KeyAssignment::QuitApplication => {
|
||||
// If we get here, there are no windows that could have received
|
||||
// the QuitApplication command, therefore it must be ok to quit
|
||||
// immediately
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
KeyAssignment::SpawnWindow => {
|
||||
spawn_command(&SpawnCommand::default(), SpawnWhere::NewWindow);
|
||||
}
|
||||
KeyAssignment::SpawnTab(spawn_where) => {
|
||||
spawn_command(
|
||||
&SpawnCommand {
|
||||
domain: spawn_where,
|
||||
..Default::default()
|
||||
},
|
||||
SpawnWhere::NewWindow,
|
||||
);
|
||||
}
|
||||
KeyAssignment::SpawnCommandInNewTab(spawn) => {
|
||||
spawn_command(&spawn, SpawnWhere::NewTab);
|
||||
}
|
||||
KeyAssignment::SpawnCommandInNewWindow(spawn) => {
|
||||
spawn_command(&spawn, SpawnWhere::NewWindow);
|
||||
}
|
||||
_ => {
|
||||
log::warn!("unhandled perform: {action:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
ApplicationEvent::OpenInBrowser(url) => {
|
||||
std::thread::spawn(move || {
|
||||
|
@ -45,6 +45,7 @@ mod scripting;
|
||||
mod scrollbar;
|
||||
mod selection;
|
||||
mod shapecache;
|
||||
mod spawn;
|
||||
mod stats;
|
||||
mod tabbar;
|
||||
mod termwindow;
|
||||
|
145
wezterm-gui/src/spawn.rs
Normal file
145
wezterm-gui/src/spawn.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use config::keyassignment::SpawnCommand;
|
||||
use config::TermConfig;
|
||||
use mux::activity::Activity;
|
||||
use mux::domain::SplitSource;
|
||||
use mux::tab::SplitRequest;
|
||||
use mux::window::WindowId as MuxWindowId;
|
||||
use mux::Mux;
|
||||
use portable_pty::CommandBuilder;
|
||||
use std::sync::Arc;
|
||||
use wezterm_term::TerminalSize;
|
||||
|
||||
#[derive(Copy, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SpawnWhere {
|
||||
NewWindow,
|
||||
NewTab,
|
||||
SplitPane(SplitRequest),
|
||||
}
|
||||
|
||||
pub fn spawn_command_impl(
|
||||
spawn: &SpawnCommand,
|
||||
spawn_where: SpawnWhere,
|
||||
size: TerminalSize,
|
||||
src_window_id: Option<MuxWindowId>,
|
||||
term_config: Arc<TermConfig>,
|
||||
) {
|
||||
let spawn = spawn.clone();
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) =
|
||||
spawn_command_internal(spawn, spawn_where, size, src_window_id, term_config).await
|
||||
{
|
||||
log::error!("Failed to spawn: {:#}", err);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub async fn spawn_command_internal(
|
||||
spawn: SpawnCommand,
|
||||
spawn_where: SpawnWhere,
|
||||
size: TerminalSize,
|
||||
src_window_id: Option<MuxWindowId>,
|
||||
term_config: Arc<TermConfig>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mux = Mux::get();
|
||||
let activity = Activity::new();
|
||||
|
||||
let current_pane_id = match src_window_id {
|
||||
Some(window_id) => {
|
||||
if let Some(tab) = mux.get_active_tab_for_window(window_id) {
|
||||
tab.get_active_pane().map(|p| p.pane_id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let cwd = if let Some(cwd) = spawn.cwd.as_ref() {
|
||||
Some(cwd.to_str().map(|s| s.to_owned()).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Domain::spawn requires that the cwd be unicode in {:?}",
|
||||
cwd
|
||||
)
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cmd_builder = if let Some(args) = spawn.args {
|
||||
let mut builder = CommandBuilder::from_argv(args.iter().map(Into::into).collect());
|
||||
for (k, v) in spawn.set_environment_variables.iter() {
|
||||
builder.env(k, v);
|
||||
}
|
||||
if let Some(cwd) = spawn.cwd {
|
||||
builder.cwd(cwd);
|
||||
}
|
||||
Some(builder)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let workspace = mux.active_workspace().clone();
|
||||
|
||||
match spawn_where {
|
||||
SpawnWhere::SplitPane(direction) => {
|
||||
let src_window_id = match src_window_id {
|
||||
Some(id) => id,
|
||||
None => anyhow::bail!("no src window when splitting a pane?"),
|
||||
};
|
||||
if let Some(tab) = mux.get_active_tab_for_window(src_window_id) {
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("tab to have a pane"))?;
|
||||
|
||||
log::trace!("doing split_pane");
|
||||
let (pane, _size) = mux
|
||||
.split_pane(
|
||||
// tab.tab_id(),
|
||||
pane.pane_id(),
|
||||
direction,
|
||||
SplitSource::Spawn {
|
||||
command: cmd_builder,
|
||||
command_dir: cwd,
|
||||
},
|
||||
spawn.domain,
|
||||
)
|
||||
.await
|
||||
.context("split_pane")?;
|
||||
pane.set_config(term_config);
|
||||
} else {
|
||||
bail!("there is no active tab while splitting pane!?");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let (_tab, pane, window_id) = mux
|
||||
.spawn_tab_or_window(
|
||||
match spawn_where {
|
||||
SpawnWhere::NewWindow => None,
|
||||
_ => src_window_id,
|
||||
},
|
||||
spawn.domain,
|
||||
cmd_builder,
|
||||
cwd,
|
||||
size,
|
||||
current_pane_id,
|
||||
workspace,
|
||||
)
|
||||
.await
|
||||
.context("spawn_tab_or_window")?;
|
||||
|
||||
// If it was created in this window, it copies our handlers.
|
||||
// Otherwise, we'll pick them up when we later respond to
|
||||
// the new window being created.
|
||||
if Some(window_id) == src_window_id {
|
||||
pane.set_config(term_config);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drop(activity);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -78,8 +78,8 @@ pub mod resize;
|
||||
mod selection;
|
||||
pub mod spawn;
|
||||
pub mod webgpu;
|
||||
use crate::spawn::SpawnWhere;
|
||||
use prevcursor::PrevCursorPos;
|
||||
use spawn::SpawnWhere;
|
||||
|
||||
const ATLAS_SIZE: usize = 128;
|
||||
|
||||
@ -2669,11 +2669,11 @@ impl TermWindow {
|
||||
let src_window_id = self.mux_window_id;
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) = Self::spawn_command_internal(
|
||||
if let Err(err) = crate::spawn::spawn_command_internal(
|
||||
spawn,
|
||||
SpawnWhere::NewWindow,
|
||||
size,
|
||||
src_window_id,
|
||||
Some(src_window_id),
|
||||
term_config,
|
||||
)
|
||||
.await
|
||||
|
@ -1,21 +1,7 @@
|
||||
use crate::termwindow::MuxWindowId;
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use crate::spawn::SpawnWhere;
|
||||
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
||||
use config::TermConfig;
|
||||
use mux::activity::Activity;
|
||||
use mux::domain::SplitSource;
|
||||
use mux::tab::SplitRequest;
|
||||
use mux::Mux;
|
||||
use portable_pty::CommandBuilder;
|
||||
use std::sync::Arc;
|
||||
use wezterm_term::TerminalSize;
|
||||
|
||||
#[derive(Copy, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SpawnWhere {
|
||||
NewWindow,
|
||||
NewTab,
|
||||
SplitPane(SplitRequest),
|
||||
}
|
||||
|
||||
impl super::TermWindow {
|
||||
pub fn spawn_command(&self, spawn: &SpawnCommand, spawn_where: SpawnWhere) {
|
||||
@ -26,126 +12,13 @@ impl super::TermWindow {
|
||||
};
|
||||
let term_config = Arc::new(TermConfig::with_config(self.config.clone()));
|
||||
|
||||
Self::spawn_command_impl(spawn, spawn_where, size, self.mux_window_id, term_config)
|
||||
}
|
||||
|
||||
fn spawn_command_impl(
|
||||
spawn: &SpawnCommand,
|
||||
spawn_where: SpawnWhere,
|
||||
size: TerminalSize,
|
||||
src_window_id: MuxWindowId,
|
||||
term_config: Arc<TermConfig>,
|
||||
) {
|
||||
let spawn = spawn.clone();
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) =
|
||||
Self::spawn_command_internal(spawn, spawn_where, size, src_window_id, term_config)
|
||||
.await
|
||||
{
|
||||
log::error!("Failed to spawn: {:#}", err);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub async fn spawn_command_internal(
|
||||
spawn: SpawnCommand,
|
||||
spawn_where: SpawnWhere,
|
||||
size: TerminalSize,
|
||||
src_window_id: MuxWindowId,
|
||||
term_config: Arc<TermConfig>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mux = Mux::get();
|
||||
let activity = Activity::new();
|
||||
|
||||
let current_pane_id = if let Some(tab) = mux.get_active_tab_for_window(src_window_id) {
|
||||
tab.get_active_pane().map(|p| p.pane_id())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cwd = if let Some(cwd) = spawn.cwd.as_ref() {
|
||||
Some(cwd.to_str().map(|s| s.to_owned()).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Domain::spawn requires that the cwd be unicode in {:?}",
|
||||
cwd
|
||||
)
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cmd_builder = if let Some(args) = spawn.args {
|
||||
let mut builder = CommandBuilder::from_argv(args.iter().map(Into::into).collect());
|
||||
for (k, v) in spawn.set_environment_variables.iter() {
|
||||
builder.env(k, v);
|
||||
}
|
||||
if let Some(cwd) = spawn.cwd {
|
||||
builder.cwd(cwd);
|
||||
}
|
||||
Some(builder)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let workspace = mux.active_workspace().clone();
|
||||
|
||||
match spawn_where {
|
||||
SpawnWhere::SplitPane(direction) => {
|
||||
if let Some(tab) = mux.get_active_tab_for_window(src_window_id) {
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("tab to have a pane"))?;
|
||||
|
||||
log::trace!("doing split_pane");
|
||||
let (pane, _size) = mux
|
||||
.split_pane(
|
||||
// tab.tab_id(),
|
||||
pane.pane_id(),
|
||||
direction,
|
||||
SplitSource::Spawn {
|
||||
command: cmd_builder,
|
||||
command_dir: cwd,
|
||||
},
|
||||
spawn.domain,
|
||||
)
|
||||
.await
|
||||
.context("split_pane")?;
|
||||
pane.set_config(term_config);
|
||||
} else {
|
||||
bail!("there is no active tab while splitting pane!?");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let (_tab, pane, window_id) = mux
|
||||
.spawn_tab_or_window(
|
||||
match spawn_where {
|
||||
SpawnWhere::NewWindow => None,
|
||||
_ => Some(src_window_id),
|
||||
},
|
||||
spawn.domain,
|
||||
cmd_builder,
|
||||
cwd,
|
||||
size,
|
||||
current_pane_id,
|
||||
workspace,
|
||||
)
|
||||
.await
|
||||
.context("spawn_tab_or_window")?;
|
||||
|
||||
// If it was created in this window, it copies our handlers.
|
||||
// Otherwise, we'll pick them up when we later respond to
|
||||
// the new window being created.
|
||||
if window_id == src_window_id {
|
||||
pane.set_config(term_config);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drop(activity);
|
||||
|
||||
Ok(())
|
||||
crate::spawn::spawn_command_impl(
|
||||
spawn,
|
||||
spawn_where,
|
||||
size,
|
||||
Some(self.mux_window_id),
|
||||
term_config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn spawn_tab(&mut self, domain: &SpawnTabDomain) {
|
||||
|
Loading…
Reference in New Issue
Block a user