Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Identify modifiers on Wayland/X11 (new) #5850

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion termwiz/src/escape/csi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn csi_size() {
assert_eq!(std::mem::size_of::<Cursor>(), 12);
assert_eq!(std::mem::size_of::<Edit>(), 8);
assert_eq!(std::mem::size_of::<Mode>(), 24);
assert_eq!(std::mem::size_of::<MouseReport>(), 8);
assert_eq!(std::mem::size_of::<MouseReport>(), 12);
assert_eq!(std::mem::size_of::<Window>(), 40);
assert_eq!(std::mem::size_of::<Keyboard>(), 8);
assert_eq!(std::mem::size_of::<CSI>(), 32);
Expand Down
68 changes: 50 additions & 18 deletions wezterm-input-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ bitflags! {
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
#[derive(Default, FromDynamic, ToDynamic)]
#[dynamic(into="String", try_from="String")]
pub struct Modifiers: u16 {
pub struct Modifiers: u32 {
const NONE = 0;
const SHIFT = 1<<1;
const ALT = 1<<2;
Expand All @@ -495,6 +495,11 @@ bitflags! {
const LEFT_SHIFT = 1<<10;
const RIGHT_SHIFT = 1<<11;
const ENHANCED_KEY = 1<<12;

const META = 1<<13;
const HYPER = 1<<14;
const CAPS_LOCK = 1<<15;
const NUM_LOCK = 1<<16;
}
}

Expand Down Expand Up @@ -1674,13 +1679,20 @@ impl KeyEvent {
if raw_modifiers.contains(Modifiers::SUPER) {
modifiers |= 8;
}
// TODO: Hyper and Meta are not handled yet.
// We should somehow detect this?
// See: https://github.com/wez/wezterm/pull/4605#issuecomment-1823604708
if self.leds.contains(KeyboardLedStatus::CAPS_LOCK) {
if raw_modifiers.contains(Modifiers::HYPER) {
modifiers |= 16;
}
if raw_modifiers.contains(Modifiers::META) {
modifiers |= 32;
}
if raw_modifiers.contains(Modifiers::CAPS_LOCK)
|| self.leds.contains(KeyboardLedStatus::CAPS_LOCK)
{
modifiers |= 64;
}
if self.leds.contains(KeyboardLedStatus::NUM_LOCK) {
if raw_modifiers.contains(Modifiers::NUM_LOCK)
|| self.leds.contains(KeyboardLedStatus::NUM_LOCK)
{
modifiers |= 128;
}
modifiers += 1;
Expand Down Expand Up @@ -1749,7 +1761,11 @@ impl KeyEvent {
});

if let Some(numpad) = is_numpad {
let code = match (numpad, self.leds.contains(KeyboardLedStatus::NUM_LOCK)) {
let code = match (
numpad,
self.leds.contains(KeyboardLedStatus::NUM_LOCK)
|| raw_modifiers.contains(Modifiers::NUM_LOCK),
) {
(PhysKeyCode::Keypad0, true) => 57399,
(PhysKeyCode::Keypad0, false) => 57425,
(PhysKeyCode::Keypad1, true) => 57400,
Expand Down Expand Up @@ -1814,26 +1830,25 @@ impl KeyEvent {
&& event_type.is_empty()
&& is_legacy_key
&& !(flags.contains(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES)
&& (self.modifiers.contains(Modifiers::CTRL)
|| self.modifiers.contains(Modifiers::ALT)))
&& !self.modifiers.intersects(
Modifiers::SUPER, /* TODO: Hyper and Meta should be added here. */
&& (raw_modifiers.contains(Modifiers::CTRL)
|| raw_modifiers.contains(Modifiers::ALT)))
&& !raw_modifiers.intersects(
Modifiers::SUPER
| Modifiers::META
| Modifiers::NUM_LOCK
| Modifiers::CAPS_LOCK,
);

if use_legacy {
// Legacy text key
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-text-keys
let mut output = String::new();
if self.modifiers.contains(Modifiers::ALT) {
if raw_modifiers.contains(Modifiers::ALT) {
output.push('\x1b');
}

if self.modifiers.contains(Modifiers::CTRL) {
csi_u_encode(
&mut output,
shifted_key.to_ascii_uppercase(),
self.modifiers,
);
if raw_modifiers.contains(Modifiers::CTRL) {
csi_u_encode(&mut output, shifted_key.to_ascii_uppercase(), raw_modifiers);
} else {
output.push(shifted_key);
}
Expand Down Expand Up @@ -2700,6 +2715,23 @@ mod test {
.encode_kitty(flags),
"\u{1b}[57399;129u".to_string()
);
assert_eq!(
make_event_with_raw(
KeyEvent {
key: KeyCode::Numpad(0),
modifiers: Modifiers::NUM_LOCK,
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad0)
)
.encode_kitty(flags),
"\u{1b}[57399;129u".to_string()
);
assert_eq!(
make_event_with_raw(
KeyEvent {
Expand Down
1 change: 1 addition & 0 deletions window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ wezterm-bidi = { path = "../bidi" }
wezterm-color-types = { path = "../color-types" }
wezterm-font = { path = "../wezterm-font" }
wezterm-input-types = { path = "../wezterm-input-types" }
scopeguard = "1.2.0"

[target."cfg(windows)".dependencies]
clipboard-win = "2.2"
Expand Down
3 changes: 3 additions & 0 deletions window/src/os/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,9 @@ fn key_modifiers(flags: NSEventModifierFlags) -> Modifiers {
if flags.contains(NSEventModifierFlags::NSCommandKeyMask) {
mods |= Modifiers::SUPER;
}
if flags.contains(NSEventModifierFlags::NSAlphaShiftKeyMask) {
mods |= Modifiers::CAPS_LOCK;
}

mods
}
Expand Down
129 changes: 128 additions & 1 deletion window/src/os/wayland/connection.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
#![allow(dead_code)]
use super::pointer::*;
use super::window::*;
use crate::connection::ConnectionOps;
use crate::os::wayland::inputhandler::InputHandler;
use crate::os::wayland::output::OutputHandler;
use crate::os::x11::keyboard::KeyboardWithFallback;
use crate::screen::{ScreenInfo, Screens};
use crate::spawn::*;
use crate::x11::ModifierInit;
use crate::{Appearance, Connection, ScreenRect, WindowEvent};
use anyhow::{bail, Context};
use mio::unix::SourceFd;
use mio::{Events, Interest, Poll, Token};
use smithay_client_toolkit as toolkit;
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::fd::AsRawFd;
Expand Down Expand Up @@ -43,7 +58,119 @@ impl WaylandConnection {
wayland_state: RefCell::new(wayland_state),
};

Ok(wayland_connection)
fn keyboard_event(
&self,
keyboard: Main<WlKeyboard>,
event: WlKeyboardEvent,
) -> anyhow::Result<()> {
match &event {
WlKeyboardEvent::Enter {
serial, surface, ..
} => {
// update global active surface id
*self.active_surface_id.borrow_mut() = surface.as_ref().id();

*self.last_serial.borrow_mut() = *serial;
if let Some(&window_id) = self
.surface_to_window_id
.borrow()
.get(&surface.as_ref().id())
{
self.keyboard_window_id.borrow_mut().replace(window_id);
self.environment.with_inner(|env| {
if let Some(input) =
env.input_handler.get_text_input_for_keyboard(&keyboard)
{
input.enable();
input.commit();
}
env.input_handler.advise_surface(&surface, &keyboard);
});
} else {
log::warn!("{:?}, no known surface", event);
}
}
WlKeyboardEvent::Leave { serial, .. } => {
if let Some(input) = self
.environment
.with_inner(|env| env.input_handler.get_text_input_for_keyboard(&keyboard))
{
input.disable();
input.commit();
}
*self.last_serial.borrow_mut() = *serial;
}
WlKeyboardEvent::Key { serial, .. } | WlKeyboardEvent::Modifiers { serial, .. } => {
*self.last_serial.borrow_mut() = *serial;
}
WlKeyboardEvent::RepeatInfo { rate, delay } => {
*self.key_repeat_rate.borrow_mut() = *rate;
*self.key_repeat_delay.borrow_mut() = *delay;
}
WlKeyboardEvent::Keymap { format, fd, size } => {
let mut file = unsafe { std::fs::File::from_raw_fd(*fd) };
match format {
KeymapFormat::XkbV1 => {
let mut data = vec![0u8; *size as usize];
// If we weren't passed a pipe, be sure to explicitly
// read from the start of the file
match file.read_exact_at(&mut data, 0) {
Ok(_) => {}
Err(err) => {
// ideally: we check for:
// err.kind() == std::io::ErrorKind::NotSeekable
// but that is not yet in stable rust
if err.raw_os_error() == Some(libc::ESPIPE) {
// It's a pipe, which cannot be seeked, so we
// just try reading from the current pipe position
file.read(&mut data).context("read from Keymap fd/pipe")?;
} else {
return Err(err).context("read_exact_at from Keymap fd");
}
}
}
// Dance around CString panicing on the NUL terminator
// in the xkbcommon crate
while let Some(0) = data.last() {
data.pop();
}
let s = String::from_utf8(data)?;
match KeyboardWithFallback::new_from_string(s, ModifierInit::Wayland) {
Ok(k) => {
self.keyboard_mapper.replace(Some(k));
}
Err(err) => {
log::error!("Error processing keymap change: {:#}", err);
}
}
}
_ => {}
}
}
_ => {}
}
if let Some(&window_id) = self.keyboard_window_id.borrow().as_ref() {
if let Some(win) = self.window_by_id(window_id) {
let mut inner = win.borrow_mut();
inner.keyboard_event(event);
}
}

Ok(())
}

pub(crate) fn dispatch_to_focused_window(&self, event: WindowEvent) {
if let Some(&window_id) = self.keyboard_window_id.borrow().as_ref() {
if let Some(win) = self.window_by_id(window_id) {
let mut inner = win.borrow_mut();
inner.events.dispatch(event);
}
}
}

pub(crate) fn next_window_id(&self) -> usize {
self.next_window_id
.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)
}

pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
Expand Down
2 changes: 2 additions & 0 deletions window/src/os/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2592,9 +2592,11 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L

let mut leds = KeyboardLedStatus::empty();
if keys[VK_CAPITAL as usize] & 1 != 0 {
modifiers |= Modifiers::CAPS_LOCK;
leds |= KeyboardLedStatus::CAPS_LOCK;
}
if keys[VK_NUMLOCK as usize] & 1 != 0 {
modifiers |= Modifiers::NUM_LOCK;
leds |= KeyboardLedStatus::NUM_LOCK;
}

Expand Down
6 changes: 3 additions & 3 deletions window/src/os/x11/connection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::keyboard::{Keyboard, KeyboardWithFallback};
use super::keyboard::{Keyboard, KeyboardWithFallback, ModifierInit};
use crate::connection::ConnectionOps;
use crate::os::x11::window::XWindowInner;
use crate::os::x11::xsettings::*;
Expand Down Expand Up @@ -712,8 +712,8 @@ impl XConnection {
visual.green_mask(),
visual.blue_mask()
);
let (keyboard, kbd_ev) = Keyboard::new(&conn)?;
let keyboard = KeyboardWithFallback::new(keyboard)?;
let (keyboard, kbd_ev) = Keyboard::new(&conn, ModifierInit::X11(&conn))?;
let keyboard = KeyboardWithFallback::new(keyboard, ModifierInit::X11(&conn))?;

let cursor_font_id = conn.generate_id();
let cursor_font_name = "cursor";
Expand Down
Loading
Loading