diff --git a/Cargo.lock b/Cargo.lock index a898feefe..aff4516f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2662,6 +2662,7 @@ dependencies = [ "mux", "portable-pty", "smol", + "termwiz", "wezterm-dynamic", "wezterm-term", ] diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index db82ca95a..73e7c9592 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -531,6 +531,8 @@ pub enum KeyAssignment { SplitPane(SplitPane), PaneSelect(PaneSelectArguments), CharSelect(CharSelectArguments), + + ResetTerminal, } impl_lua_conversion_dynamic!(KeyAssignment); diff --git a/docs/changelog.md b/docs/changelog.md index 1295ca16a..84dffb611 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -39,6 +39,8 @@ As features stabilize some brief notes about them will accumulate here. [#2537](https://github.com/wez/wezterm/discussions/2537) * [window-focus-changed](config/lua/window-events/window-focus-changed.md) event. +* [pane:inject_output](config/lua/pane/inject_output.md) method +* [ResetTerminal](config/lua/keyassignment/ResetTerminal.md) key assignment #### Fixed * Wayland: key repeat gets stuck after pressing two keys in quick succession. diff --git a/docs/config/lua/keyassignment/ResetTerminal.md b/docs/config/lua/keyassignment/ResetTerminal.md new file mode 100644 index 000000000..f9e79647a --- /dev/null +++ b/docs/config/lua/keyassignment/ResetTerminal.md @@ -0,0 +1,11 @@ +# ResetTerminal + +*since: nightly builds only* + +Sends the `RIS` "Reset to Initial State" escape sequence (`ESC-c`) to the +output side of the current pane, causing the terminal emulator to reset its +state. + +This will reset tab stops, margins, modes, graphic rendition, palette, activate +the primary screen, erase the display and move the cursor to the home position. + diff --git a/docs/config/lua/pane/inject_output.md b/docs/config/lua/pane/inject_output.md new file mode 100644 index 000000000..d345144ce --- /dev/null +++ b/docs/config/lua/pane/inject_output.md @@ -0,0 +1,34 @@ +# `pane:inject_output(text)` + +*Since: nightly builds only* + +Sends text, which may include escape sequences, to the output side of the +current pane. The text will be evaluated by the terminal emulator and can thus +be used to inject/force the terminal to process escape sequences that adjust +the current mode, as well as sending human readable output to the terminal. + +Note that if you move the cursor position as a result of using this method, you +should expect the display to change and for text UI programs to get confused. + +In this contrived and useless example, pressing ALT-k will output `hello there` +in italics to the current pane: + +```lua +local wezterm = require 'wezterm' + +return { + keys = { + { + key = 'k', + mods = 'ALT', + action = wezterm.action_callback(function(window, pane) + pane:inject_output '\r\n\x1b[3mhello there\r\n' + end), + }, + }, +} +``` + +Not all panes support this method; at the time of writing, this works for local +panes but not for multiplexer panes. + diff --git a/lua-api-crates/mux/Cargo.toml b/lua-api-crates/mux/Cargo.toml index a90daa4b8..437ce0903 100644 --- a/lua-api-crates/mux/Cargo.toml +++ b/lua-api-crates/mux/Cargo.toml @@ -15,4 +15,5 @@ log = "0.4" luahelper = { path = "../../luahelper" } portable-pty = { path = "../../pty" } smol = "1.2" +termwiz = { path = "../../termwiz" } mux = { path = "../../mux" } diff --git a/lua-api-crates/mux/src/pane.rs b/lua-api-crates/mux/src/pane.rs index decdedfa9..051fc964b 100644 --- a/lua-api-crates/mux/src/pane.rs +++ b/lua-api-crates/mux/src/pane.rs @@ -196,6 +196,18 @@ impl UserData for MuxPane { None => Ok("".to_string()), } }); + + methods.add_method("inject_output", |_, this, text: String| { + let mux = get_mux()?; + let pane = this.resolve(&mux)?; + + let mut parser = termwiz::escape::parser::Parser::new(); + let mut actions = vec![]; + parser.parse(text.as_bytes(), |action| actions.push(action)); + + pane.perform_actions(actions); + Ok(()) + }); } } diff --git a/wezterm-gui/src/scripting/guiwin.rs b/wezterm-gui/src/scripting/guiwin.rs index d61aecb35..df30d0875 100644 --- a/wezterm-gui/src/scripting/guiwin.rs +++ b/wezterm-gui/src/scripting/guiwin.rs @@ -244,6 +244,25 @@ impl UserData for GuiWin { Ok(result) }); + methods.add_async_method("active_pane", |_, this, _: ()| async move { + let (tx, rx) = smol::channel::bounded(1); + this.window + .notify(TermWindowNotif::Apply(Box::new(move |term_window| { + tx.try_send( + term_window + .get_active_pane_or_overlay() + .map(|pane| MuxPane(pane.pane_id())), + ) + .ok(); + }))); + let result = rx + .recv() + .await + .map_err(|e| anyhow::anyhow!("{:#}", e)) + .map_err(luaerr)?; + + Ok(result) + }); methods.add_method("active_workspace", |_, _, _: ()| { let mux = Mux::get() .ok_or_else(|| anyhow::anyhow!("must be called on main thread")) diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 3aea4ceb0..e5bf3eebf 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2719,6 +2719,11 @@ impl TermWindow { let modal = crate::termwindow::charselect::CharSelector::new(self, args); self.modal.borrow_mut().replace(Rc::new(modal)); } + ResetTerminal => { + pane.perform_actions(vec![termwiz::escape::Action::Esc( + termwiz::escape::Esc::Code(termwiz::escape::EscCode::FullReset), + )]); + } }; Ok(PerformAssignmentResult::Handled) } @@ -2953,7 +2958,7 @@ impl TermWindow { /// then that will be returned instead. Otherwise, if the pane has /// an active overlay (such as search or copy mode) then that will /// be returned. - fn get_active_pane_or_overlay(&self) -> Option> { + pub fn get_active_pane_or_overlay(&self) -> Option> { let mux = Mux::get().unwrap(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { Some(tab) => tab,