From c5f270f48b0601ec64e08c54eee06a6517998a05 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Fri, 5 Jun 2020 07:25:35 -0700 Subject: [PATCH] wezterm: windows: automatically add WSL entries to the launcher menu This feels like a better out of the box experience. The list can be disabled through the config if desired. --- docs/changelog.markdown | 6 +++ docs/config/launch.markdown | 4 ++ src/config/mod.rs | 3 ++ src/frontend/gui/overlay/launcher.rs | 64 +++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/docs/changelog.markdown b/docs/changelog.markdown index 19e11a435..fdfe5e4ee 100644 --- a/docs/changelog.markdown +++ b/docs/changelog.markdown @@ -29,6 +29,12 @@ brief notes about them may accumulate here. You can switch to the old behavior either by explicitly binding those keys or by setting `send_composed_key_when_alt_is_pressed = false` in your configuration file. +* Windows: the launcher menu now automatically lists out any WSL environments + you have installed so that you can quickly spawn a shall in any of them. + You can suppress this behavior if you wish by setting + `add_wsl_distributions_to_launch_menu = false`. + [Read more about the launcher menuu](config/launch.html#the-launcher-menu) + ### 20200517-122836-92c201c6 diff --git a/docs/config/launch.markdown b/docs/config/launch.markdown index c093bfa04..a26cfa150 100644 --- a/docs/config/launch.markdown +++ b/docs/config/launch.markdown @@ -117,6 +117,10 @@ return { Here's a fancy example that will add some helpful entries to the launcher menu when running on Windows: +*since: latest nightly*: The launcher menu automatically includes WSL +entries by default, unless disabled using `add_wsl_distributions_to_launch_menu = false`. + + ```lua local wezterm = require 'wezterm'; diff --git a/src/config/mod.rs b/src/config/mod.rs index 2fba6269a..9849435a7 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -511,6 +511,9 @@ pub struct Config { #[serde(default)] pub launch_menu: Vec, + #[serde(default = "default_true")] + pub add_wsl_distributions_to_launch_menu: bool, + #[serde(default = "default_true")] pub check_for_updates: bool, diff --git a/src/frontend/gui/overlay/launcher.rs b/src/frontend/gui/overlay/launcher.rs index 4f8002fdc..2460dd554 100644 --- a/src/frontend/gui/overlay/launcher.rs +++ b/src/frontend/gui/overlay/launcher.rs @@ -43,6 +43,59 @@ impl Entry { } } +#[cfg(windows)] +fn enumerate_wsl_entries(entries: &mut Vec) -> anyhow::Result<()> { + use std::os::windows::process::CommandExt; + let mut cmd = std::process::Command::new("wsl.exe"); + cmd.arg("-l"); + cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW); + let output = cmd.output()?; + + let stderr = String::from_utf8_lossy(&output.stderr); + anyhow::ensure!( + output.status.success(), + "wsl -l command invocation failed: {}", + stderr + ); + + /// Ungh: https://github.com/microsoft/WSL/issues/4456 + fn utf16_to_utf8(bytes: &[u8]) -> anyhow::Result { + if bytes.len() % 2 != 0 { + anyhow::bail!("input data has odd length, cannot be utf16"); + } + + // This is "safe" because we checked that the length seems reasonable, + // and our new slice is within those same bounds. + let wide: &[u16] = + unsafe { std::slice::from_raw_parts(bytes.as_ptr() as *const u16, bytes.len() / 2) }; + + String::from_utf16(wide).map_err(|_| anyhow!("wsl -l output is not valid utf16")) + } + + let wsl_list = utf16_to_utf8(&output.stdout)?.replace("\r\n", "\n"); + for line in wsl_list.lines().skip(1) { + // Remove the "(Default)" marker, if present, to leave just the distro name + let distro = line.replace(" (Default)", ""); + let label = format!("{} (WSL)", distro); + + entries.push(Entry::Spawn { + label: label.clone(), + command: SpawnCommand { + label: Some(label), + args: Some(vec![ + "wsl.exe".to_owned(), + "--distribution".to_owned(), + distro.to_owned(), + ]), + ..Default::default() + }, + new_window: false, + }); + } + + Ok(()) +} + pub fn launcher( _tab_id: TabId, domain_id_of_current_tab: DomainId, @@ -57,9 +110,11 @@ pub fn launcher( term.set_raw_mode()?; + let config = configuration(); + // Pull in the user defined entries from the launch_menu // section of the configuration. - for item in &configuration().launch_menu { + for item in &config.launch_menu { entries.push(Entry::Spawn { label: match item.label.as_ref() { Some(label) => label.to_string(), @@ -73,6 +128,13 @@ pub fn launcher( }); } + #[cfg(windows)] + { + if config.add_wsl_distributions_to_launch_menu { + let _ = enumerate_wsl_entries(&mut entries); + } + } + for (domain_id, domain_state, domain_name) in &domains { let entry = if *domain_state == DomainState::Attached { Entry::Spawn {