1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 22:01:47 +03:00

allow binding opt + key based on pre-composed key presses

This diff adds some plumbing to track the `raw_key` in the KeyEvent;
this is the key prior to composing or eg: mapping dead keys.

With that field in place, we can teach the termwindow layer to attempt
looking up that key mapping from the user defined key bindings.

If we get a match then we can stop further key processing.
This commit is contained in:
Wez Furlong 2019-11-05 21:32:23 -08:00
parent 11137aa613
commit eb1bc7f736
5 changed files with 80 additions and 26 deletions

View File

@ -184,14 +184,19 @@ impl WindowCallbacks for TermWindow {
return false; return false;
} }
let mux = Mux::get().unwrap(); // log::error!("key_event {:?}", key);
if let Some(tab) = mux.get_active_tab_for_window(self.mux_window_id) {
let modifiers = window_mods_to_termwiz_mods(key.modifiers);
enum Key {
Code(::termwiz::input::KeyCode),
Composed(String),
None,
}
fn win_key_code_to_termwiz_key_code(key: &::window::KeyCode) -> Key {
use ::termwiz::input::KeyCode as KC; use ::termwiz::input::KeyCode as KC;
use ::window::KeyCode as WK; use ::window::KeyCode as WK;
let key = match key.key { let code = match key {
// TODO: consider eliminating these codes from termwiz::input::KeyCode // TODO: consider eliminating these codes from termwiz::input::KeyCode
WK::Char('\r') => KC::Enter, WK::Char('\r') => KC::Enter,
WK::Char('\t') => KC::Tab, WK::Char('\t') => KC::Tab,
@ -199,12 +204,11 @@ impl WindowCallbacks for TermWindow {
WK::Char('\u{1b}') => KC::Escape, WK::Char('\u{1b}') => KC::Escape,
WK::Char('\u{7f}') => KC::Delete, WK::Char('\u{7f}') => KC::Delete,
WK::Char(c) => KC::Char(c), WK::Char(c) => KC::Char(*c),
WK::Composed(ref s) => { WK::Composed(ref s) => {
tab.writer().write_all(s.as_bytes()).ok(); return Key::Composed(s.to_owned());
return true;
} }
WK::Function(f) => KC::Function(f), WK::Function(f) => KC::Function(*f),
WK::LeftArrow => KC::LeftArrow, WK::LeftArrow => KC::LeftArrow,
WK::RightArrow => KC::RightArrow, WK::RightArrow => KC::RightArrow,
WK::UpArrow => KC::UpArrow, WK::UpArrow => KC::UpArrow,
@ -251,7 +255,7 @@ impl WindowCallbacks for TermWindow {
WK::Numpad(7) => KC::Numpad7, WK::Numpad(7) => KC::Numpad7,
WK::Numpad(8) => KC::Numpad8, WK::Numpad(8) => KC::Numpad8,
WK::Numpad(9) => KC::Numpad9, WK::Numpad(9) => KC::Numpad9,
WK::Numpad(_) => return false, WK::Numpad(_) => return Key::None,
WK::Separator => KC::Separator, WK::Separator => KC::Separator,
WK::Subtract => KC::Subtract, WK::Subtract => KC::Subtract,
WK::Decimal => KC::Decimal, WK::Decimal => KC::Decimal,
@ -277,7 +281,27 @@ impl WindowCallbacks for TermWindow {
WK::ApplicationUpArrow => KC::ApplicationUpArrow, WK::ApplicationUpArrow => KC::ApplicationUpArrow,
WK::ApplicationDownArrow => KC::ApplicationDownArrow, WK::ApplicationDownArrow => KC::ApplicationDownArrow,
}; };
Key::Code(code)
}
let mux = Mux::get().unwrap();
if let Some(tab) = mux.get_active_tab_for_window(self.mux_window_id) {
let modifiers = window_mods_to_termwiz_mods(key.modifiers);
// First chance to operate on the raw key; if it matches a
// user-defined key binding then we execute it and stop there.
if let Some(key) = &key.raw_key {
if let Key::Code(key) = win_key_code_to_termwiz_key_code(&key) {
if let Some(assignment) = self.keys.lookup(key, modifiers) {
self.perform_key_assignment(&tab, &assignment).ok();
return true;
}
}
}
let key = win_key_code_to_termwiz_key_code(&key.key);
match key {
Key::Code(key) => {
if let Some(assignment) = self.keys.lookup(key, modifiers) { if let Some(assignment) = self.keys.lookup(key, modifiers) {
self.perform_key_assignment(&tab, &assignment).ok(); self.perform_key_assignment(&tab, &assignment).ok();
return true; return true;
@ -285,6 +309,13 @@ impl WindowCallbacks for TermWindow {
return true; return true;
} }
} }
Key::Composed(s) => {
tab.writer().write_all(s.as_bytes()).ok();
return true;
}
Key::None => {}
}
}
false false
} }

View File

@ -135,9 +135,15 @@ pub struct MouseEvent {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyEvent { pub struct KeyEvent {
/// Which key was pressed /// Which key was pressed.
/// This is the potentially processed/composed version
/// of the input.
pub key: KeyCode, pub key: KeyCode,
/// The raw unprocessed key press if it was different from
/// the processed/composed version
pub raw_key: Option<KeyCode>,
/// Which modifiers are down /// Which modifiers are down
pub modifiers: Modifiers, pub modifiers: Modifiers,

View File

@ -226,6 +226,9 @@ pub(crate) struct WindowInner {
} }
fn function_key_to_keycode(function_key: char) -> KeyCode { fn function_key_to_keycode(function_key: char) -> KeyCode {
// FIXME: CTRL-C is 0x3, should it be normalized to C here
// using the unmod string? Or should be normalize the 0x3
// as the canonical representation of that input?
use cocoa::appkit; use cocoa::appkit;
match function_key as u16 { match function_key as u16 {
appkit::NSUpArrowFunctionKey => KeyCode::UpArrow, appkit::NSUpArrowFunctionKey => KeyCode::UpArrow,
@ -638,6 +641,7 @@ impl WindowView {
let event = KeyEvent { let event = KeyEvent {
key, key,
raw_key: None,
modifiers: Modifiers::default(), modifiers: Modifiers::default(),
repeat_count: 1, repeat_count: 1,
key_is_down: true, key_is_down: true,
@ -673,6 +677,7 @@ impl WindowView {
let event = KeyEvent { let event = KeyEvent {
key: KeyCode::Composed(s.to_string()), key: KeyCode::Composed(s.to_string()),
raw_key: None,
modifiers: Modifiers::default(), modifiers: Modifiers::default(),
repeat_count: 1, repeat_count: 1,
key_is_down: true, key_is_down: true,
@ -866,7 +871,7 @@ impl WindowView {
fn key_common(this: &mut Object, nsevent: id, key_is_down: bool) { fn key_common(this: &mut Object, nsevent: id, key_is_down: bool) {
let is_a_repeat = unsafe { nsevent.isARepeat() == YES }; let is_a_repeat = unsafe { nsevent.isARepeat() == YES };
let chars = unsafe { nsstring_to_str(nsevent.characters()) }; let chars = unsafe { nsstring_to_str(nsevent.characters()) };
// let unmod = unsafe { nsstring_to_str(nsevent.charactersIgnoringModifiers()) }; let unmod = unsafe { nsstring_to_str(nsevent.charactersIgnoringModifiers()) };
let modifiers = unsafe { key_modifiers(nsevent.modifierFlags()) }; let modifiers = unsafe { key_modifiers(nsevent.modifierFlags()) };
if modifiers.is_empty() && !is_a_repeat { if modifiers.is_empty() && !is_a_repeat {
@ -879,21 +884,30 @@ impl WindowView {
} }
} }
let mut char_iter = chars.chars(); fn key_string_to_key_code(s: &str) -> Option<KeyCode> {
let mut char_iter = s.chars();
if let Some(first_char) = char_iter.next() { if let Some(first_char) = char_iter.next() {
let key = if char_iter.next().is_none() { if char_iter.next().is_none() {
// A single unicode char // A single unicode char
function_key_to_keycode(first_char) Some(function_key_to_keycode(first_char))
} else { } else {
KeyCode::Composed(chars.to_owned()) Some(KeyCode::Composed(s.to_owned()))
}; }
} else {
None
}
}
// FIXME: CTRL-C is 0x3, should it be normalized to C here if let Some(key) = key_string_to_key_code(chars) {
// using the unmod string? Or should be normalize the 0x3 let raw_key = if chars == unmod {
// as the canonical representation of that input? None
} else {
key_string_to_key_code(unmod)
};
let event = KeyEvent { let event = KeyEvent {
key, key,
raw_key,
modifiers, modifiers,
repeat_count: 1, repeat_count: 1,
key_is_down, key_is_down,

View File

@ -758,6 +758,7 @@ unsafe fn ime_composition(
Ok(s) => { Ok(s) => {
let key = KeyEvent { let key = KeyEvent {
key: KeyCode::Composed(s), key: KeyCode::Composed(s),
raw_key: None,
modifiers: Modifiers::default(), modifiers: Modifiers::default(),
repeat_count: 1, repeat_count: 1,
key_is_down: true, key_is_down: true,
@ -945,6 +946,7 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
if let Some(key) = key { if let Some(key) = key {
let key = KeyEvent { let key = KeyEvent {
key, key,
raw_key: None,
modifiers, modifiers,
repeat_count: repeat, repeat_count: repeat,
key_is_down: !releasing, key_is_down: !releasing,

View File

@ -261,6 +261,7 @@ impl WindowInner {
if let Some((code, mods)) = self.conn.keyboard.process_key_event(key_press) { if let Some((code, mods)) = self.conn.keyboard.process_key_event(key_press) {
let key = KeyEvent { let key = KeyEvent {
key: code, key: code,
raw_key: None,
modifiers: mods, modifiers: mods,
repeat_count: 1, repeat_count: 1,
key_is_down: r == xcb::KEY_PRESS, key_is_down: r == xcb::KEY_PRESS,