1
1
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:
Wez Furlong 2022-12-21 00:24:11 -07:00
parent f7eb13dd8d
commit 4b3660d166
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
6 changed files with 218 additions and 155 deletions

View File

@ -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))?,

View File

@ -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 || {

View File

@ -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
View 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(())
}

View File

@ -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

View File

@ -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) {