From 075388fbe5653392d010b23476f9cf179554d4d0 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sun, 16 Apr 2023 19:41:59 -0700 Subject: [PATCH] add window:keyboard_modifiers() method --- docs/changelog.md | 4 +++ docs/config/lua/window/keyboard_modifiers.md | 30 ++++++++++++++++++++ wezterm-gui/src/scripting/guiwin.rs | 15 ++++++++++ wezterm-gui/src/termwindow/keyevent.rs | 14 ++++++++- wezterm-gui/src/termwindow/mod.rs | 8 ++++++ wezterm-input-types/src/lib.rs | 16 +++++++++++ window/examples/async.rs | 1 + window/src/lib.rs | 2 ++ window/src/os/macos/window.rs | 22 ++++++++++++++ 9 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 docs/config/lua/window/keyboard_modifiers.md diff --git a/docs/changelog.md b/docs/changelog.md index a4c2d6a61..8508e8353 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -21,6 +21,10 @@ usually the best available version. As features stabilize some brief notes about them will accumulate here. +#### New + +* [window:keyboard_modifiers](config/lua/window/keyboard_modifiers.md) #3444 + #### Fixed * Modals, such as `CharSelect` and `CommandPalette` did not respect alternative diff --git a/docs/config/lua/window/keyboard_modifiers.md b/docs/config/lua/window/keyboard_modifiers.md new file mode 100644 index 000000000..bcc8d308f --- /dev/null +++ b/docs/config/lua/window/keyboard_modifiers.md @@ -0,0 +1,30 @@ +# window:keyboard_modifiers() + +{{since('nightly')}} + +Returns two values; the keyboard modifiers and the key status leds. + +Both values are exposed to lua as strings with `|`-delimited values +representing the various modifier keys and keyboard led states: + +* Modifiers - is the same form as keyboard assignment modifiers +* Leds - possible flags are `"CAPS_LOCK"` and `"NUM_LOCK"`. Note that macOS + doesn't have a num lock concept. + +This example shows the current modifier and led status in the right status +area: + +```lua +local wezterm = require 'wezterm' + +local config = wezterm.config_builder() + +config.debug_key_events = true + +wezterm.on('update-status', function(window, pane) + local mods, leds = window:keyboard_modifiers() + window:set_right_status('mods=' .. mods .. ' leds=' .. leds) +end) + +return config +``` diff --git a/wezterm-gui/src/scripting/guiwin.rs b/wezterm-gui/src/scripting/guiwin.rs index 7e805b95c..58a41a7a4 100644 --- a/wezterm-gui/src/scripting/guiwin.rs +++ b/wezterm-gui/src/scripting/guiwin.rs @@ -257,6 +257,21 @@ impl UserData for GuiWin { Ok(result) }); + methods.add_async_method("keyboard_modifiers", |_, 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.current_modifier_and_led_state()) + .ok(); + }))); + let (mods, leds) = rx + .recv() + .await + .map_err(|e| anyhow::anyhow!("{:#}", e)) + .map_err(luaerr)?; + + Ok((mods.to_string(), leds.to_string())) + }); methods.add_async_method("active_pane", |_, this, _: ()| async move { let (tx, rx) = smol::channel::bounded(1); this.window diff --git a/wezterm-gui/src/termwindow/keyevent.rs b/wezterm-gui/src/termwindow/keyevent.rs index fd2670f9e..e12b5ab08 100644 --- a/wezterm-gui/src/termwindow/keyevent.rs +++ b/wezterm-gui/src/termwindow/keyevent.rs @@ -1,5 +1,7 @@ use crate::termwindow::InputMap; -use ::window::{DeadKeyStatus, KeyCode, KeyEvent, Modifiers, RawKeyEvent, WindowOps}; +use ::window::{ + DeadKeyStatus, KeyCode, KeyEvent, KeyboardLedStatus, Modifiers, RawKeyEvent, WindowOps, +}; use anyhow::Context; use config::keyassignment::{KeyAssignment, KeyTableEntry}; use mux::pane::{Pane, PerformAssignmentResult}; @@ -448,6 +450,12 @@ impl super::TermWindow { ); } + let modifier_and_leds = (key.modifiers, key.leds); + if self.current_modifier_and_leds != modifier_and_leds { + self.current_modifier_and_leds = modifier_and_leds; + self.schedule_next_status_update(); + } + let pane = match self.get_active_pane_or_overlay() { Some(pane) => pane, None => return, @@ -517,6 +525,10 @@ impl super::TermWindow { } } + pub fn current_modifier_and_led_state(&self) -> (Modifiers, KeyboardLedStatus) { + self.current_modifier_and_leds + } + pub fn leader_is_active(&self) -> bool { match self.leader_is_down.as_ref() { Some(expiry) if *expiry > std::time::Instant::now() => { diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 5e8dc97e3..1f0b4f4aa 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -395,6 +395,7 @@ pub struct TermWindow { window_background: Vec, + current_modifier_and_leds: (Modifiers, KeyboardLedStatus), current_mouse_buttons: Vec, current_mouse_capture: Option, @@ -694,6 +695,7 @@ impl TermWindow { last_mouse_coords: (0, -1), window_drag_position: None, current_mouse_event: None, + current_modifier_and_leds: Default::default(), prev_cursor: PrevCursorPos::new(), last_scroll_info: RenderableDimensions::default(), tab_state: RefCell::new(HashMap::new()), @@ -925,6 +927,12 @@ impl TermWindow { self.resize(dimensions, window_state, window, live_resizing); Ok(true) } + WindowEvent::AdviseModifiersLedStatus(modifiers, leds) => { + self.current_modifier_and_leds = (modifiers, leds); + self.update_title(); + window.invalidate(); + Ok(true) + } WindowEvent::RawKeyEvent(event) => { self.raw_key_event_impl(event, window); Ok(true) diff --git a/wezterm-input-types/src/lib.rs b/wezterm-input-types/src/lib.rs index eef675728..020947cd4 100644 --- a/wezterm-input-types/src/lib.rs +++ b/wezterm-input-types/src/lib.rs @@ -459,6 +459,22 @@ bitflags! { } } +impl ToString for KeyboardLedStatus { + fn to_string(&self) -> String { + let mut s = String::new(); + if self.contains(Self::CAPS_LOCK) { + s.push_str("CAPS_LOCK"); + } + if self.contains(Self::NUM_LOCK) { + if !s.is_empty() { + s.push('|'); + } + s.push_str("NUM_LOCK"); + } + s + } +} + bitflags! { #[cfg_attr(feature="serde", derive(Serialize, Deserialize))] #[derive(Default, FromDynamic, ToDynamic)] diff --git a/window/examples/async.rs b/window/examples/async.rs index 50afe6784..ceaa372cb 100644 --- a/window/examples/async.rs +++ b/window/examples/async.rs @@ -80,6 +80,7 @@ impl MyWindow { } WindowEvent::AppearanceChanged(_) | WindowEvent::AdviseDeadKeyStatus(_) + | WindowEvent::AdviseModifiersLedStatus(_, _) | WindowEvent::Notification(_) | WindowEvent::FocusChanged(_) | WindowEvent::DraggedFile(_) diff --git a/window/src/lib.rs b/window/src/lib.rs index 8a4c43a38..a2512d024 100644 --- a/window/src/lib.rs +++ b/window/src/lib.rs @@ -189,6 +189,8 @@ pub enum WindowEvent { /// Called by menubar dispatching stuff on some systems PerformKeyAssignment(config::keyassignment::KeyAssignment), + + AdviseModifiersLedStatus(Modifiers, KeyboardLedStatus), } pub struct WindowEventSender { diff --git a/window/src/os/macos/window.rs b/window/src/os/macos/window.rs index 15433a862..127fed723 100644 --- a/window/src/os/macos/window.rs +++ b/window/src/os/macos/window.rs @@ -2657,6 +2657,23 @@ impl WindowView { } } + extern "C" fn flags_changed(this: &mut Object, _sel: Sel, nsevent: id) { + let modifier_flags = unsafe { nsevent.modifierFlags() }; + let modifiers = key_modifiers(modifier_flags); + let leds = if modifier_flags.bits() & (1 << 16) != 0 { + KeyboardLedStatus::CAPS_LOCK + } else { + KeyboardLedStatus::empty() + }; + + if let Some(myself) = Self::get_this(this) { + let mut inner = myself.inner.borrow_mut(); + inner + .events + .dispatch(WindowEvent::AdviseModifiersLedStatus(modifiers, leds)); + } + } + extern "C" fn key_down(this: &mut Object, _sel: Sel, nsevent: id) { Self::key_common(this, nsevent, true); } @@ -3101,6 +3118,11 @@ impl WindowView { Self::update_tracking_areas as extern "C" fn(&mut Object, Sel), ); + cls.add_method( + sel!(flagsChanged:), + Self::flags_changed as extern "C" fn(&mut Object, Sel, id), + ); + // NSTextInputClient cls.add_method(