mirror of
https://github.com/wez/wezterm.git
synced 2024-11-22 13:16:39 +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>
|
<string>An application launched via WezTerm would like to access your Downloads folder.</string>
|
||||||
<key>NSSystemAdministrationUsageDescription</key>
|
<key>NSSystemAdministrationUsageDescription</key>
|
||||||
<string>An application launched via WezTerm requires elevated permission.</string>
|
<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>
|
</dict>
|
||||||
</plist>
|
</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
|
* 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)!
|
a time. Thanks to [@pengux](https://github.com/pengux)!
|
||||||
[#2662](https://github.com/wez/wezterm/pull/2662)
|
[#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
|
#### Changed
|
||||||
* Window title reporting escape sequences are now disabled by default.
|
* Window title reporting escape sequences are now disabled by default.
|
||||||
|
@ -32,6 +32,7 @@ impl Drop for GuiFrontEnd {
|
|||||||
impl GuiFrontEnd {
|
impl GuiFrontEnd {
|
||||||
pub fn try_new() -> anyhow::Result<Rc<GuiFrontEnd>> {
|
pub fn try_new() -> anyhow::Result<Rc<GuiFrontEnd>> {
|
||||||
let connection = Connection::init()?;
|
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 mux = Mux::get().expect("mux started and running on main thread");
|
||||||
let client_id = mux.active_identity().expect("to have set my own id");
|
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()),
|
known_windows: RefCell::new(BTreeMap::new()),
|
||||||
client_id: client_id.clone(),
|
client_id: client_id.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let fe = Rc::downgrade(&front_end);
|
let fe = Rc::downgrade(&front_end);
|
||||||
mux.subscribe(move |n| {
|
mux.subscribe(move |n| {
|
||||||
if let Some(fe) = fe.upgrade() {
|
if let Some(fe) = fe.upgrade() {
|
||||||
@ -152,6 +154,53 @@ impl GuiFrontEnd {
|
|||||||
Ok(front_end)
|
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<()> {
|
pub fn run_forever(&self) -> anyhow::Result<()> {
|
||||||
self.connection
|
self.connection
|
||||||
.run_message_loop()
|
.run_message_loop()
|
||||||
|
@ -4,15 +4,26 @@ use anyhow::Result as Fallible;
|
|||||||
use config::DimensionContext;
|
use config::DimensionContext;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static CONN: RefCell<Option<Rc<Connection>>> = RefCell::new(None);
|
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() {
|
pub fn shutdown() {
|
||||||
CONN.with(|m| drop(m.borrow_mut().take()));
|
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 {
|
pub trait ConnectionOps {
|
||||||
fn get() -> Option<Rc<Connection>> {
|
fn get() -> Option<Rc<Connection>> {
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
@ -24,6 +35,16 @@ pub trait ConnectionOps {
|
|||||||
res
|
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 {
|
fn default_dpi(&self) -> f64 {
|
||||||
crate::DEFAULT_DPI
|
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::nsstring_to_str;
|
||||||
use super::window::WindowInner;
|
use super::window::WindowInner;
|
||||||
use crate::connection::ConnectionOps;
|
use crate::connection::ConnectionOps;
|
||||||
|
use crate::os::macos::app::create_app_delegate;
|
||||||
use crate::screen::{ScreenInfo, Screens};
|
use crate::screen::{ScreenInfo, Screens};
|
||||||
use crate::spawn::*;
|
use crate::spawn::*;
|
||||||
use crate::Appearance;
|
use crate::Appearance;
|
||||||
@ -33,6 +34,10 @@ impl Connection {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let ns_app = NSApp();
|
let ns_app = NSApp();
|
||||||
ns_app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
|
ns_app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
|
||||||
|
|
||||||
|
let delegate = create_app_delegate();
|
||||||
|
let () = msg_send![ns_app, setDelegate: delegate];
|
||||||
|
|
||||||
let conn = Self {
|
let conn = Self {
|
||||||
ns_app,
|
ns_app,
|
||||||
windows: RefCell::new(HashMap::new()),
|
windows: RefCell::new(HashMap::new()),
|
||||||
|
@ -4,6 +4,7 @@ use objc::rc::StrongPtr;
|
|||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc::*;
|
use objc::*;
|
||||||
|
|
||||||
|
mod app;
|
||||||
pub mod bitmap;
|
pub mod bitmap;
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
Loading…
Reference in New Issue
Block a user