mirror of
https://github.com/wez/wezterm.git
synced 2024-09-11 14:25:57 +03:00
macos: allow association with .command file type
Implement an app delegate to receive a callback when the system requests that we open `.command` files, and then ask the mux layer to spawn a window for it and execute it via the shell. Also allow handling `.sh`, `.zsh`, `.bash`, `.fish` and `.tool`, per kitty. refs: https://github.com/wez/wezterm/issues/2741 refs: https://github.com/wez/wezterm/issues/2871
This commit is contained in:
parent
e70f97903b
commit
28c7c0ba19
@ -60,5 +60,43 @@
|
||||
<string>An application launched via WezTerm would like to access your Downloads folder.</string>
|
||||
<key>NSSystemAdministrationUsageDescription</key>
|
||||
<string>An application launched via WezTerm requires elevated permission.</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Terminal shell script</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>terminal.icns</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>command</string>
|
||||
<string>sh</string>
|
||||
<string>zsh</string>
|
||||
<string>bash</string>
|
||||
<string>fish</string>
|
||||
<string>tool</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Folders</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>fold</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Shell</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.unix-executable</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -15,6 +15,10 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* Copy Mode now supports using `CTRL-u` and `CTRL-d` to move by half a page at
|
||||
a time. Thanks to [@pengux](https://github.com/pengux)!
|
||||
[#2662](https://github.com/wez/wezterm/pull/2662)
|
||||
* macOS: allow association with `.command`, `.sh`, `.zsh`, `.bash`, `.fish` and
|
||||
`.tool` scripts, so that those can open and show their output in the
|
||||
terminal. [#2871](https://github.com/wez/wezterm/issues/2871)
|
||||
[#2741](https://github.com/wez/wezterm/issues/2741)
|
||||
|
||||
#### Changed
|
||||
* Window title reporting escape sequences are now disabled by default.
|
||||
|
@ -32,6 +32,7 @@ impl Drop for GuiFrontEnd {
|
||||
impl GuiFrontEnd {
|
||||
pub fn try_new() -> anyhow::Result<Rc<GuiFrontEnd>> {
|
||||
let connection = Connection::init()?;
|
||||
connection.set_event_handler(Self::app_event_handler);
|
||||
|
||||
let mux = Mux::get().expect("mux started and running on main thread");
|
||||
let client_id = mux.active_identity().expect("to have set my own id");
|
||||
@ -43,6 +44,7 @@ impl GuiFrontEnd {
|
||||
known_windows: RefCell::new(BTreeMap::new()),
|
||||
client_id: client_id.clone(),
|
||||
});
|
||||
|
||||
let fe = Rc::downgrade(&front_end);
|
||||
mux.subscribe(move |n| {
|
||||
if let Some(fe) = fe.upgrade() {
|
||||
@ -152,6 +154,53 @@ impl GuiFrontEnd {
|
||||
Ok(front_end)
|
||||
}
|
||||
|
||||
fn app_event_handler(event: ApplicationEvent) {
|
||||
log::trace!("Got app event {event:?}");
|
||||
match event {
|
||||
ApplicationEvent::OpenCommandScript(file_name) => {
|
||||
promise::spawn::spawn(async move {
|
||||
use config::keyassignment::SpawnTabDomain;
|
||||
use portable_pty::CommandBuilder;
|
||||
use wezterm_term::TerminalSize;
|
||||
|
||||
let cmd = CommandBuilder::from_argv(
|
||||
["/bin/sh", "-c", &file_name]
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let mux = Mux::get().expect("mux started");
|
||||
let window_id = None;
|
||||
let pane_id = None;
|
||||
let cwd = None;
|
||||
let workspace = mux.active_workspace();
|
||||
|
||||
match mux
|
||||
.spawn_tab_or_window(
|
||||
window_id,
|
||||
SpawnTabDomain::DomainName("local".to_string()),
|
||||
Some(cmd),
|
||||
cwd,
|
||||
TerminalSize::default(),
|
||||
pane_id,
|
||||
workspace,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((_tab, pane, _window_id)) => {
|
||||
log::trace!("Spawned {file_name} as pane_id {}", pane.pane_id());
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to spawn {file_name}: {err:#?}");
|
||||
}
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever(&self) -> anyhow::Result<()> {
|
||||
self.connection
|
||||
.run_message_loop()
|
||||
|
@ -4,15 +4,26 @@ use anyhow::Result as Fallible;
|
||||
use config::DimensionContext;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
thread_local! {
|
||||
static CONN: RefCell<Option<Rc<Connection>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
fn nop_event_handler(_event: ApplicationEvent) {}
|
||||
|
||||
static EVENT_HANDLER: Mutex<fn(ApplicationEvent)> = Mutex::new(nop_event_handler);
|
||||
|
||||
pub fn shutdown() {
|
||||
CONN.with(|m| drop(m.borrow_mut().take()));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ApplicationEvent {
|
||||
/// The system wants to open a command in the terminal
|
||||
OpenCommandScript(String),
|
||||
}
|
||||
|
||||
pub trait ConnectionOps {
|
||||
fn get() -> Option<Rc<Connection>> {
|
||||
let mut res = None;
|
||||
@ -24,6 +35,16 @@ pub trait ConnectionOps {
|
||||
res
|
||||
}
|
||||
|
||||
fn set_event_handler(&self, func: fn(ApplicationEvent)) {
|
||||
let mut handler = EVENT_HANDLER.lock().unwrap();
|
||||
*handler = func;
|
||||
}
|
||||
|
||||
fn dispatch_app_event(&self, event: ApplicationEvent) {
|
||||
let func = EVENT_HANDLER.lock().unwrap();
|
||||
func(event);
|
||||
}
|
||||
|
||||
fn default_dpi(&self) -> f64 {
|
||||
crate::DEFAULT_DPI
|
||||
}
|
||||
|
72
window/src/os/macos/app.rs
Normal file
72
window/src/os/macos/app.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use crate::connection::ConnectionOps;
|
||||
use crate::macos::nsstring_to_str;
|
||||
use crate::{ApplicationEvent, Connection};
|
||||
use cocoa::appkit::NSApplicationTerminateReply;
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::rc::StrongPtr;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::*;
|
||||
|
||||
const CLS_NAME: &str = "WezTermAppDelegate";
|
||||
|
||||
extern "C" fn application_should_terminate(
|
||||
_self: &mut Object,
|
||||
_sel: Sel,
|
||||
_app: *mut Object,
|
||||
) -> u64 {
|
||||
log::debug!("application termination requested");
|
||||
NSApplicationTerminateReply::NSTerminateLater as u64
|
||||
}
|
||||
|
||||
extern "C" fn application_will_finish_launching(
|
||||
_self: &mut Object,
|
||||
_sel: Sel,
|
||||
_notif: *mut Object,
|
||||
) {
|
||||
log::debug!("application_will_finish_launching");
|
||||
}
|
||||
|
||||
extern "C" fn application_open_file(
|
||||
_self: &mut Object,
|
||||
_sel: Sel,
|
||||
_app: *mut Object,
|
||||
file_name: *mut Object,
|
||||
) {
|
||||
let file_name = unsafe { nsstring_to_str(file_name) }.to_string();
|
||||
if let Some(conn) = Connection::get() {
|
||||
conn.dispatch_app_event(ApplicationEvent::OpenCommandScript(file_name));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_class() -> &'static Class {
|
||||
Class::get(CLS_NAME).unwrap_or_else(|| {
|
||||
let mut cls = ClassDecl::new(CLS_NAME, class!(NSWindow))
|
||||
.expect("Unable to register application class");
|
||||
|
||||
unsafe {
|
||||
cls.add_method(
|
||||
sel!(applicationShouldTerminate:),
|
||||
application_should_terminate as extern "C" fn(&mut Object, Sel, *mut Object) -> u64,
|
||||
);
|
||||
cls.add_method(
|
||||
sel!(applicationWillFinishLaunching:),
|
||||
application_will_finish_launching as extern "C" fn(&mut Object, Sel, *mut Object),
|
||||
);
|
||||
cls.add_method(
|
||||
sel!(application:openFile:),
|
||||
application_open_file as extern "C" fn(&mut Object, Sel, *mut Object, *mut Object),
|
||||
);
|
||||
}
|
||||
|
||||
cls.register()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_app_delegate() -> StrongPtr {
|
||||
let cls = get_class();
|
||||
unsafe {
|
||||
let delegate: *mut Object = msg_send![cls, alloc];
|
||||
let delegate: *mut Object = msg_send![delegate, init];
|
||||
StrongPtr::new(delegate)
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
use super::nsstring_to_str;
|
||||
use super::window::WindowInner;
|
||||
use crate::connection::ConnectionOps;
|
||||
use crate::os::macos::app::create_app_delegate;
|
||||
use crate::screen::{ScreenInfo, Screens};
|
||||
use crate::spawn::*;
|
||||
use crate::Appearance;
|
||||
@ -33,6 +34,10 @@ impl Connection {
|
||||
unsafe {
|
||||
let ns_app = NSApp();
|
||||
ns_app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
|
||||
|
||||
let delegate = create_app_delegate();
|
||||
let () = msg_send![ns_app, setDelegate: delegate];
|
||||
|
||||
let conn = Self {
|
||||
ns_app,
|
||||
windows: RefCell::new(HashMap::new()),
|
||||
|
@ -4,6 +4,7 @@ use objc::rc::StrongPtr;
|
||||
use objc::runtime::Object;
|
||||
use objc::*;
|
||||
|
||||
mod app;
|
||||
pub mod bitmap;
|
||||
pub mod connection;
|
||||
pub mod window;
|
||||
|
Loading…
Reference in New Issue
Block a user