mirror of
https://github.com/wez/wezterm.git
synced 2024-12-28 07:55:03 +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>> {
|
) -> anyhow::Result<Arc<dyn Domain>> {
|
||||||
let domain = match domain {
|
let domain = match domain {
|
||||||
SpawnTabDomain::DefaultDomain => self.default_domain(),
|
SpawnTabDomain::DefaultDomain => self.default_domain(),
|
||||||
SpawnTabDomain::CurrentPaneDomain => {
|
SpawnTabDomain::CurrentPaneDomain => match pane_id {
|
||||||
let pane_id = pane_id
|
Some(pane_id) => {
|
||||||
.ok_or_else(|| anyhow!("CurrentPaneDomain used with no current pane"))?;
|
let (pane_domain_id, _window_id, _tab_id) = self
|
||||||
let (pane_domain_id, _window_id, _tab_id) = self
|
.resolve_pane_id(pane_id)
|
||||||
.resolve_pane_id(pane_id)
|
.ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?;
|
||||||
.ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?;
|
self.get_domain(pane_domain_id)
|
||||||
self.get_domain(pane_domain_id)
|
.expect("resolve_pane_id to give valid domain_id")
|
||||||
.expect("resolve_pane_id to give valid domain_id")
|
}
|
||||||
}
|
None => self.default_domain(),
|
||||||
|
},
|
||||||
SpawnTabDomain::DomainId(domain_id) => self
|
SpawnTabDomain::DomainId(domain_id) => self
|
||||||
.get_domain(*domain_id)
|
.get_domain(*domain_id)
|
||||||
.ok_or_else(|| anyhow!("domain id {} is invalid", domain_id))?,
|
.ok_or_else(|| anyhow!("domain id {} is invalid", domain_id))?,
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use crate::scripting::guiwin::GuiWin;
|
use crate::scripting::guiwin::GuiWin;
|
||||||
|
use crate::spawn::SpawnWhere;
|
||||||
use crate::termwindow::TermWindowNotif;
|
use crate::termwindow::TermWindowNotif;
|
||||||
use crate::TermWindow;
|
use crate::TermWindow;
|
||||||
use ::window::*;
|
use ::window::*;
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
|
use config::keyassignment::{KeyAssignment, SpawnCommand};
|
||||||
pub use config::FrontEndSelection;
|
pub use config::FrontEndSelection;
|
||||||
use mux::client::ClientId;
|
use mux::client::ClientId;
|
||||||
use mux::window::WindowId as MuxWindowId;
|
use mux::window::WindowId as MuxWindowId;
|
||||||
@ -98,13 +100,16 @@ impl GuiFrontEnd {
|
|||||||
| Alert::SetUserVar { .. },
|
| Alert::SetUserVar { .. },
|
||||||
} => {}
|
} => {}
|
||||||
MuxNotification::Empty => {
|
MuxNotification::Empty => {
|
||||||
promise::spawn::spawn_into_main_thread(async move {
|
#[cfg(not(target_os = "macos"))]
|
||||||
if mux::activity::Activity::count() == 0 {
|
{
|
||||||
log::trace!("Mux is now empty, terminate gui");
|
promise::spawn::spawn_into_main_thread(async move {
|
||||||
Connection::get().unwrap().terminate_message_loop();
|
if mux::activity::Activity::count() == 0 {
|
||||||
}
|
log::trace!("Mux is now empty, terminate gui");
|
||||||
})
|
Connection::get().unwrap().terminate_message_loop();
|
||||||
.detach();
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MuxNotification::SaveToDownloads { name, data } => {
|
MuxNotification::SaveToDownloads { name, data } => {
|
||||||
if !config::configuration().allow_download_protocols {
|
if !config::configuration().allow_download_protocols {
|
||||||
@ -211,7 +216,45 @@ impl GuiFrontEnd {
|
|||||||
// and the user picks an action from the menubar.
|
// and the user picks an action from the menubar.
|
||||||
// This is not currently possible, but could be in the
|
// This is not currently possible, but could be in the
|
||||||
// future.
|
// 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) => {
|
ApplicationEvent::OpenInBrowser(url) => {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
@ -45,6 +45,7 @@ mod scripting;
|
|||||||
mod scrollbar;
|
mod scrollbar;
|
||||||
mod selection;
|
mod selection;
|
||||||
mod shapecache;
|
mod shapecache;
|
||||||
|
mod spawn;
|
||||||
mod stats;
|
mod stats;
|
||||||
mod tabbar;
|
mod tabbar;
|
||||||
mod termwindow;
|
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;
|
mod selection;
|
||||||
pub mod spawn;
|
pub mod spawn;
|
||||||
pub mod webgpu;
|
pub mod webgpu;
|
||||||
|
use crate::spawn::SpawnWhere;
|
||||||
use prevcursor::PrevCursorPos;
|
use prevcursor::PrevCursorPos;
|
||||||
use spawn::SpawnWhere;
|
|
||||||
|
|
||||||
const ATLAS_SIZE: usize = 128;
|
const ATLAS_SIZE: usize = 128;
|
||||||
|
|
||||||
@ -2669,11 +2669,11 @@ impl TermWindow {
|
|||||||
let src_window_id = self.mux_window_id;
|
let src_window_id = self.mux_window_id;
|
||||||
|
|
||||||
promise::spawn::spawn(async move {
|
promise::spawn::spawn(async move {
|
||||||
if let Err(err) = Self::spawn_command_internal(
|
if let Err(err) = crate::spawn::spawn_command_internal(
|
||||||
spawn,
|
spawn,
|
||||||
SpawnWhere::NewWindow,
|
SpawnWhere::NewWindow,
|
||||||
size,
|
size,
|
||||||
src_window_id,
|
Some(src_window_id),
|
||||||
term_config,
|
term_config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1,21 +1,7 @@
|
|||||||
use crate::termwindow::MuxWindowId;
|
use crate::spawn::SpawnWhere;
|
||||||
use anyhow::{anyhow, bail, Context};
|
|
||||||
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
||||||
use config::TermConfig;
|
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 std::sync::Arc;
|
||||||
use wezterm_term::TerminalSize;
|
|
||||||
|
|
||||||
#[derive(Copy, Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub enum SpawnWhere {
|
|
||||||
NewWindow,
|
|
||||||
NewTab,
|
|
||||||
SplitPane(SplitRequest),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::TermWindow {
|
impl super::TermWindow {
|
||||||
pub fn spawn_command(&self, spawn: &SpawnCommand, spawn_where: SpawnWhere) {
|
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()));
|
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)
|
crate::spawn::spawn_command_impl(
|
||||||
}
|
spawn,
|
||||||
|
spawn_where,
|
||||||
fn spawn_command_impl(
|
size,
|
||||||
spawn: &SpawnCommand,
|
Some(self.mux_window_id),
|
||||||
spawn_where: SpawnWhere,
|
term_config,
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_tab(&mut self, domain: &SpawnTabDomain) {
|
pub fn spawn_tab(&mut self, domain: &SpawnTabDomain) {
|
||||||
|
Loading…
Reference in New Issue
Block a user