diff --git a/Cargo.lock b/Cargo.lock index 2c3a17ab1..95a8a0a99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4755,9 +4755,9 @@ dependencies = [ "config", "log", "luahelper", - "open", "smol", "wezterm-dynamic", + "wezterm-open-url", "winapi", ] @@ -5987,7 +5987,6 @@ dependencies = [ "mux", "mux-lua", "once_cell", - "open", "ordered-float", "parking_lot 0.12.1", "portable-pty", @@ -6024,6 +6023,7 @@ dependencies = [ "wezterm-font", "wezterm-gui-subcommands", "wezterm-mux-server-impl", + "wezterm-open-url", "wezterm-ssh", "wezterm-term", "wezterm-toast-notification", @@ -6102,6 +6102,14 @@ dependencies = [ "winapi", ] +[[package]] +name = "wezterm-open-url" +version = "0.1.0" +dependencies = [ + "open", + "winapi", +] + [[package]] name = "wezterm-ssh" version = "0.4.0" @@ -6175,8 +6183,8 @@ dependencies = [ "futures-util", "log", "objc", - "open", "serde", + "wezterm-open-url", "windows 0.33.0", "xml-rs", "zbus", diff --git a/Cargo.toml b/Cargo.toml index 66684ef8f..984711bda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "wezterm-dynamic", "wezterm-gui", "wezterm-mux-server", + "wezterm-open-url", "wezterm-ssh" ] resolver = "2" diff --git a/lua-api-crates/spawn-funcs/Cargo.toml b/lua-api-crates/spawn-funcs/Cargo.toml index 9f6ac7416..64788a750 100644 --- a/lua-api-crates/spawn-funcs/Cargo.toml +++ b/lua-api-crates/spawn-funcs/Cargo.toml @@ -13,7 +13,7 @@ log = "0.4" luahelper = { path = "../../luahelper" } smol = "1.2" bstr = "0.2" -open = "4.0" +wezterm-open-url = { path = "../../wezterm-open-url" } [target."cfg(windows)".dependencies] winapi = { version = "0.3", features = ["winuser"]} diff --git a/lua-api-crates/spawn-funcs/src/lib.rs b/lua-api-crates/spawn-funcs/src/lib.rs index 1eae1f661..e3f3ea349 100644 --- a/lua-api-crates/spawn-funcs/src/lib.rs +++ b/lua-api-crates/spawn-funcs/src/lib.rs @@ -18,13 +18,9 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> { fn open_with<'lua>(_: &'lua Lua, (url, app): (String, Option)) -> mlua::Result<()> { if let Some(app) = app { - open::with_in_background(url, app); + wezterm_open_url::open_with(&url, &app); } else { - std::thread::spawn(move || { - if let Err(err) = open::that(&url) { - log::error!("Error opening {}: {:#}", url, err); - } - }); + wezterm_open_url::open_url(&url); } Ok(()) } diff --git a/wezterm-gui/Cargo.toml b/wezterm-gui/Cargo.toml index 5a6b9f6f4..2eaf29328 100644 --- a/wezterm-gui/Cargo.toml +++ b/wezterm-gui/Cargo.toml @@ -67,7 +67,6 @@ mlua = {version="0.8.3", features=["send"]} mux = { path = "../mux" } mux-lua = { path = "../lua-api-crates/mux" } once_cell = "1.8" -open = "4.0" ordered-float = "3.0" parking_lot = "0.12" portable-pty = { path = "../pty", features = ["serde_support"]} @@ -102,6 +101,7 @@ wezterm-dynamic = { path = "../wezterm-dynamic" } wezterm-font = { path = "../wezterm-font" } wezterm-gui-subcommands = { path = "../wezterm-gui-subcommands" } wezterm-mux-server-impl = { path = "../wezterm-mux-server-impl" } +wezterm-open-url = { path = "../wezterm-open-url" } wezterm-ssh = { path = "../wezterm-ssh" } wezterm-term = { path = "../term", features=["use_serde"] } wezterm-toast-notification = { path = "../wezterm-toast-notification" } diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 12868f48b..fd8509ee6 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2810,12 +2810,7 @@ impl TermWindow { )]); } OpenUri(link) => { - let link = link.clone(); - std::thread::spawn(move || { - if let Err(err) = open::that(&link) { - log::error!("Error opening {}: {:#}", link, err); - } - }); + wezterm_open_url::open_url(link); } ActivateCommandPalette => { let modal = crate::termwindow::palette::CommandPalette::new(self); @@ -2832,7 +2827,7 @@ impl TermWindow { // triggering our WndProc recursively. // We get that assurance for free as part of the async dispatch that we // perform below; here we allow the user to define an `open-uri` event - // handler that can bypass the normal `open::that` functionality. + // handler that can bypass the normal `open_url` functionality. if let Some(link) = self.current_highlight.as_ref().cloned() { let window = GuiWin::new(self); let pane = MuxPane(pane.pane_id()); @@ -2856,12 +2851,8 @@ impl TermWindow { None => true, }; if default_click { - std::thread::spawn(move || { - log::info!("clicking {}", link); - if let Err(err) = open::that(&link) { - log::error!("Error opening {}: {:#}", link, err); - } - }); + log::info!("clicking {}", link); + wezterm_open_url::open_url(&link); } Ok(()) } diff --git a/wezterm-open-url/Cargo.toml b/wezterm-open-url/Cargo.toml new file mode 100644 index 000000000..102c8fd1f --- /dev/null +++ b/wezterm-open-url/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wezterm-open-url" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +open = "4.0" +winapi = {version="0.3", features=["shellapi"]} diff --git a/wezterm-open-url/src/lib.rs b/wezterm-open-url/src/lib.rs new file mode 100644 index 000000000..d8ef32547 --- /dev/null +++ b/wezterm-open-url/src/lib.rs @@ -0,0 +1,57 @@ +#[cfg(not(windows))] +pub fn open_url(url: &str) { + let url = url.to_string(); + std::thread::spawn(move || { + let _ = open::that(&url); + }); +} + +#[cfg(not(windows))] +pub fn open_with(url: &str, app: &str) { + open::with_in_background(url, app); +} + +#[cfg(windows)] +fn shell_execute(url: String, with: Option) { + use std::os::windows::ffi::OsStrExt; + use winapi::um::shellapi::ShellExecuteW; + /// Convert a rust string to a windows wide string + fn wide_string(s: &str) -> Vec { + std::ffi::OsStr::new(s) + .encode_wide() + .chain(std::iter::once(0)) + .collect() + } + std::thread::spawn(move || { + let operation = wide_string("open"); + + let url = wide_string(&url); + let with = with.map(|s| wide_string(&s)); + + let (app, path) = match with { + Some(app) => (app.as_ptr(), url.as_ptr()), + None => (url.as_ptr(), std::ptr::null()), + }; + + unsafe { + ShellExecuteW( + std::ptr::null_mut(), + operation.as_ptr(), + app, + path, + std::ptr::null(), + winapi::um::winuser::SW_SHOW, + ); + } + }); +} + +#[cfg(windows)] +pub fn open_url(url: &str) { + shell_execute(url.to_string(), None); +} + +#[cfg(windows)] +pub fn open_with(url: &str, app: &str) { + shell_execute(url.to_string(), Some(app.to_string())); +} diff --git a/wezterm-toast-notification/Cargo.toml b/wezterm-toast-notification/Cargo.toml index 7bc5857de..5c1a4bf1c 100644 --- a/wezterm-toast-notification/Cargo.toml +++ b/wezterm-toast-notification/Cargo.toml @@ -8,7 +8,7 @@ build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -open = "4.0" +wezterm-open-url = { path = "../wezterm-open-url" } log = "0.4" [target.'cfg(all(not(windows), not(target_os="macos")))'.dependencies] diff --git a/wezterm-toast-notification/src/dbus.rs b/wezterm-toast-notification/src/dbus.rs index 8061b16f5..2ae2a2afe 100644 --- a/wezterm-toast-notification/src/dbus.rs +++ b/wezterm-toast-notification/src/dbus.rs @@ -126,10 +126,7 @@ async fn show_notif_impl(notif: ToastNotification) -> Result<(), Box Result<(), Box> { if args == "show" { if let Some(url) = toast.url.as_ref() { - let _ = open::that(url); + wezterm_open_url::open_url(url); } }