From 1e688a5128a280c429cfd7058caf0cf366a5416e Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Wed, 29 Mar 2023 19:05:13 -0700 Subject: [PATCH] Add serial_ports config This commit teaches the config about SerialDomains, and the mux layer about constructing a SerialDomain, then changes the GUI layer to use those pieces to set up `wezterm serial`. A new `serial_ports` config is added, and the GUI layer knows how to apply it to the set of domains in the mux. The result of this is that you can now define a domain for each serial port and spawn a serial connection into a new tab or window in your running wezterm gui instance. --- Cargo.lock | 2 +- config/src/config.rs | 7 +++- config/src/lib.rs | 2 + config/src/serial.rs | 19 +++++++++ docs/changelog.md | 1 + docs/config/lua/config/serial_ports.md | 54 ++++++++++++++++++++++++++ mux/Cargo.toml | 1 + mux/src/domain.rs | 12 +++++- wezterm-gui-subcommands/src/lib.rs | 2 +- wezterm-gui/Cargo.toml | 1 - wezterm-gui/src/main.rs | 24 ++++++++---- 11 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 config/src/serial.rs create mode 100644 docs/config/lua/config/serial_ports.md diff --git a/Cargo.lock b/Cargo.lock index 1a369ce32..eec3e2b55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3075,6 +3075,7 @@ dependencies = [ "rangeset", "regex", "serde", + "serial", "shell-words", "smol", "terminfo", @@ -6029,7 +6030,6 @@ dependencies = [ "regex", "serde", "serde_json", - "serial", "shared_library", "shlex", "smol", diff --git a/config/src/config.rs b/config/src/config.rs index 753977ad5..f98bc45f8 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -23,8 +23,8 @@ use crate::wsl::WslDomain; use crate::{ default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64, default_true, GpuInfo, KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, - WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, - HOME_DIR, + SerialDomain, WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, + CONFIG_SKIP, HOME_DIR, }; use anyhow::Context; use luahelper::impl_lua_conversion_dynamic; @@ -301,6 +301,9 @@ pub struct Config { #[dynamic(default)] pub exec_domains: Vec, + #[dynamic(default)] + pub serial_ports: Vec, + /// The set of unix domains #[dynamic(default = "UnixDomain::default_unix_domains")] pub unix_domains: Vec, diff --git a/config/src/lib.rs b/config/src/lib.rs index ee2b5dd84..4b5b6035d 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -32,6 +32,7 @@ mod keys; pub mod lua; pub mod meta; mod scheme_data; +mod serial; mod ssh; mod terminal; mod tls; @@ -49,6 +50,7 @@ pub use exec_domain::*; pub use font::*; pub use frontend::*; pub use keys::*; +pub use serial::*; pub use ssh::*; pub use terminal::*; pub use tls::*; diff --git a/config/src/serial.rs b/config/src/serial.rs new file mode 100644 index 000000000..b07c4108a --- /dev/null +++ b/config/src/serial.rs @@ -0,0 +1,19 @@ +use crate::config::validate_domain_name; +use wezterm_dynamic::{FromDynamic, ToDynamic}; + +#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)] +pub struct SerialDomain { + /// The name of this specific domain. Must be unique amongst + /// all types of domain in the configuration file. + #[dynamic(validate = "validate_domain_name")] + pub name: String, + + /// Specifies the serial device name. + /// On Windows systems this can be a name like `COM0`. + /// On posix systems this will be something like `/dev/ttyUSB0`. + /// If omitted, the name will be interpreted as the port. + pub port: Option, + + /// Set the baud rate. The default is 9600 baud. + pub baud: Option, +} diff --git a/docs/changelog.md b/docs/changelog.md index bb9f3eddf..81ab16a81 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -24,6 +24,7 @@ As features stabilize some brief notes about them will accumulate here. #### New * [pane:activate()](config/lua/pane/activate.md) and [tab:activate()](config/lua/MuxTab/activate.md). #3217 * [ulimit_nofile](config/lua/config/ulimit_nofile.md) and [ulimint_nproc](config/lua/config/ulimit_nproc.md) options. ?3353 +* [serial_ports](config/lua/config/serial_ports.md) for more convenient access to serial ports #### Fixed * mux: Stale remote window mapping could prevent spawning new tabs in remote domain. #2759 diff --git a/docs/config/lua/config/serial_ports.md b/docs/config/lua/config/serial_ports.md new file mode 100644 index 000000000..b1b344a42 --- /dev/null +++ b/docs/config/lua/config/serial_ports.md @@ -0,0 +1,54 @@ +# `serial_ports = {}` + +{{since('nightly')}} + +Define a list of serial port(s) that you use regularly. +Each entry defines a `SerialDomain` with the following fields: + +* `name` - the name to use for the serial domain. Must be unique across + all multiplexer domains in your configuration. +* `port` - the name of the serial device. On Windows systems this can be + a name like `COM0`. On Posix systems this will be a device path something + like `/dev/ttyUSB0`. If omitted, the `name` field be be interpreted as + the port name. +* `baud` - the communication speed to assign to the port. If omitted, + the default baud rate will be 9600. + +This configuration defines a single port: + +```lua +config.serial_ports = { + { + name = '/dev/tty.usbserial-10', + baud = 115200, + }, +} +``` + +You can then use the port in one of the following ways: + +* `wezterm connect /dev/tty.usbserial-10` - this behaves similarly to `wezterm + serial /dev/tty.usbserial-10 --baud 115200`. +* Start wezterm normally, then use the Command Palette or Launcher Menu to + spawn a new tab in the `/dev/tty.usbserial-10` domain to connect to the + serial device +* You can reference the serial domain by its name `/dev/tty.usbserial-10` in + the various tab/window spawning key assignments that include a + [SpawnCommand](../SpawnCommand.md) + +You can define multiple ports if you require, and use friendly name for them: + +```lua +config.serial_ports = { + { + name = 'Sensor 1', + port = '/dev/tty.usbserial-10', + baud = 115200, + }, + { + name = 'Sensor 2', + port = '/dev/tty.usbserial-11', + baud = 115200, + }, +} +``` diff --git a/mux/Cargo.toml b/mux/Cargo.toml index 153aa52b1..70f726ee8 100644 --- a/mux/Cargo.toml +++ b/mux/Cargo.toml @@ -35,6 +35,7 @@ promise = { path = "../promise" } rangeset = { path = "../rangeset" } regex = "1" serde = {version="1.0", features = ["rc", "derive"]} +serial = "0.4" shell-words = "1.1" smol = "1.2" terminfo = "0.8" diff --git a/mux/src/domain.rs b/mux/src/domain.rs index fe311d4c2..51c1383e9 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -13,7 +13,7 @@ use crate::Mux; use anyhow::{bail, Context, Error}; use async_trait::async_trait; use config::keyassignment::{SpawnCommand, SpawnTabDomain}; -use config::{configuration, ExecDomain, ValueOrFunc, WslDomain}; +use config::{configuration, ExecDomain, SerialDomain, ValueOrFunc, WslDomain}; use downcast_rs::{impl_downcast, Downcast}; use parking_lot::Mutex; use portable_pty::{native_pty_system, CommandBuilder, PtySystem}; @@ -229,6 +229,16 @@ impl LocalDomain { Self::new(&exec_domain.name) } + pub fn new_serial_domain(serial_domain: SerialDomain) -> anyhow::Result { + let port = serial_domain.port.as_ref().unwrap_or(&serial_domain.name); + let mut serial = portable_pty::serial::SerialTty::new(&port); + if let Some(baud) = serial_domain.baud { + serial.set_baud_rate(serial::BaudRate::from_speed(baud)); + } + let pty_system = Box::new(serial); + Ok(Self::with_pty_system(&serial_domain.name, pty_system)) + } + #[cfg(unix)] fn is_conpty(&self) -> bool { false diff --git a/wezterm-gui-subcommands/src/lib.rs b/wezterm-gui-subcommands/src/lib.rs index 61847d170..a69f4b241 100644 --- a/wezterm-gui-subcommands/src/lib.rs +++ b/wezterm-gui-subcommands/src/lib.rs @@ -193,7 +193,7 @@ pub struct SerialCommand { /// On Windows systems this can be a name like `COM0`. /// On posix systems this will be something like `/dev/ttyUSB0` #[arg(value_parser)] - pub port: OsString, + pub port: String, } #[derive(Debug, Parser, Clone)] diff --git a/wezterm-gui/Cargo.toml b/wezterm-gui/Cargo.toml index 14397fe4b..b61b1164d 100644 --- a/wezterm-gui/Cargo.toml +++ b/wezterm-gui/Cargo.toml @@ -77,7 +77,6 @@ ratelim= { path = "../ratelim" } regex = "1" serde = {version="1.0", features = ["rc", "derive"]} serde_json = "1.0" -serial = "0.4" shlex = "1.1" smol = "1.2" tabout = { path = "../tabout" } diff --git a/wezterm-gui/src/main.rs b/wezterm-gui/src/main.rs index f73b9e8a5..3466ede62 100644 --- a/wezterm-gui/src/main.rs +++ b/wezterm-gui/src/main.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, Context}; use clap::builder::ValueParser; use clap::{Parser, ValueHint}; use config::keyassignment::{SpawnCommand, SpawnTabDomain}; -use config::{ConfigHandle, SshDomain, SshMultiplexing}; +use config::{ConfigHandle, SerialDomain, SshDomain, SshMultiplexing}; use mux::activity::Activity; use mux::domain::{Domain, LocalDomain}; use mux::ssh::RemoteSshDomain; @@ -201,13 +201,14 @@ fn run_serial(config: config::ConfigHandle, opts: &SerialCommand) -> anyhow::Res set_window_position(pos.clone()); } - let mut serial = portable_pty::serial::SerialTty::new(&opts.port); - if let Some(baud) = opts.baud { - serial.set_baud_rate(serial::BaudRate::from_speed(baud)); - } + let serial_domain = SerialDomain { + name: "local".into(), + port: Some(opts.port.clone()), + baud: opts.baud, + }; + + let domain: Arc = Arc::new(LocalDomain::new_serial_domain(serial_domain)?); - let pty_system = Box::new(serial); - let domain: Arc = Arc::new(LocalDomain::with_pty_system("local", pty_system)); let mux = setup_mux(domain.clone(), &config, Some("local"), None)?; let gui = crate::frontend::try_new()?; @@ -354,6 +355,15 @@ fn update_mux_domains(config: &ConfigHandle) -> anyhow::Result<()> { mux.add_domain(&domain); } + for serial in &config.serial_ports { + if mux.get_domain_by_name(&serial.name).is_some() { + continue; + } + + let domain: Arc = Arc::new(LocalDomain::new_serial_domain(serial.clone())?); + mux.add_domain(&domain); + } + if let Some(name) = &config.default_domain { if let Some(dom) = mux.get_domain_by_name(name) { mux.set_default_domain(&dom);