1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-19 18:57:59 +03:00

gui: revise win32-input-mode flow

We need 100% of the info for it to work correctly, so this commit:

* Exposes the keyboard encoding mode via the Pane trait
* Adds the scan code to the RawKeyEvent
* Has the GUI perform the encoding if the keyboard is set that way
* Removes the basic encoder from termwiz in favor of the gui level one

The net result is that we bypass the Pane::key_up/Pane::key_down methods
in almost all cases when the encoding mode is set to win32-input-mode.

There is now a config option: allow_win32_input_mode that can be
used to prevent using this mode.

refs: #1509
This commit is contained in:
Wez Furlong 2022-01-07 12:49:33 -07:00
parent 4efcc81bee
commit 4524abcdba
11 changed files with 145 additions and 108 deletions

View File

@ -1257,6 +1257,9 @@ pub struct Config {
#[serde(default = "default_true")]
pub allow_download_protocols: bool,
#[serde(default = "default_true")]
pub allow_win32_input_mode: bool,
}
impl_lua_conversion!(Config);

View File

@ -18,6 +18,7 @@ use std::ops::Range;
use std::sync::Arc;
use std::time::{Duration, Instant};
use termwiz::escape::DeviceControlMode;
use termwiz::input::KeyboardEncoding;
use termwiz::surface::{Line, SequenceNo, SEQ_ZERO};
use url::Url;
use wezterm_term::color::ColorPalette;
@ -71,6 +72,10 @@ impl Pane for LocalPane {
cursor
}
fn get_keyboard_encoding(&self) -> KeyboardEncoding {
self.terminal.borrow().get_keyboard_encoding()
}
fn get_current_seqno(&self) -> SequenceNo {
self.terminal.borrow().current_seqno()
}

View File

@ -12,6 +12,7 @@ use std::collections::HashMap;
use std::ops::Range;
use std::sync::{Arc, Mutex};
use termwiz::hyperlink::Rule;
use termwiz::input::KeyboardEncoding;
use termwiz::surface::{Line, SequenceNo, SEQ_ZERO};
use url::Url;
use wezterm_term::color::ColorPalette;
@ -330,6 +331,10 @@ pub trait Pane: Downcast {
fn palette(&self) -> ColorPalette;
fn domain_id(&self) -> DomainId;
fn get_keyboard_encoding(&self) -> KeyboardEncoding {
KeyboardEncoding::Xterm
}
fn copy_user_vars(&self) -> HashMap<String, String> {
HashMap::new()
}

View File

@ -2393,4 +2393,8 @@ impl TerminalState {
pub fn get_reverse_video(&self) -> bool {
self.reverse_video_mode
}
pub fn get_keyboard_encoding(&self) -> KeyboardEncoding {
self.keyboard_encoding
}
}

View File

@ -251,87 +251,12 @@ impl KeyCode {
)
}
/// <https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md>
/// We only encode a handful of specific keys where we know that we can
/// translate between the VK_XXX value and our own representation,
/// and where it resolves potential ambiguity, or allows passing through
/// key down events for modifier keys themselves.
/// We don't have enough information here to represent all possible
/// key presses correctly in the win32-input-mode encoding.
pub fn encode_win32_input_mode(&self, mods: Modifiers, is_down: bool) -> Option<String> {
use KeyCode::*;
// <https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes>
const VK_BACK: usize = 0x08;
const VK_SPACE: usize = 0x20;
const VK_RETURN: usize = 0x0d;
const VK_F1: usize = 0x70;
const VK_SHIFT: usize = 0x10;
const VK_CONTROL: usize = 0x11;
const VK_MENU: usize = 0x12;
// Note that we normalize left/right modifiers to just the main
// VK for that key type, so that eg: powershell doesn't get
// confused and interpret the record as an `@` key event.
let (vkey, uni) = match self {
Char(' ') => (VK_SPACE, 0x20),
Enter => (VK_RETURN, 0x0d),
Backspace => (VK_BACK, 0x0a),
Function(n) if *n >= 1 && *n <= 24 => ((*n as usize - 1) + VK_F1, 0x0),
LeftShift | Shift | RightShift => (VK_SHIFT, 0x0),
LeftControl | Control | RightControl => (VK_CONTROL, 0x0),
LeftAlt | Alt | RightAlt => (VK_MENU, 0x0),
_ => return None,
};
// <https://docs.microsoft.com/en-us/windows/console/key-event-record-str>
// defines the dwControlKeyState values
let mut control_key_state = 0;
const SHIFT_PRESSED: usize = 0x10;
const RIGHT_ALT_PRESSED: usize = 0x01;
const LEFT_ALT_PRESSED: usize = 0x02;
const LEFT_CTRL_PRESSED: usize = 0x08;
const RIGHT_CTRL_PRESSED: usize = 0x04;
if mods.contains(Modifiers::SHIFT) {
control_key_state |= SHIFT_PRESSED;
}
if mods.contains(Modifiers::ALT) {
if *self == RightAlt {
control_key_state |= RIGHT_ALT_PRESSED;
} else {
control_key_state |= LEFT_ALT_PRESSED;
}
}
if mods.contains(Modifiers::CTRL) {
if *self == RightControl {
control_key_state |= RIGHT_CTRL_PRESSED;
} else {
control_key_state |= LEFT_CTRL_PRESSED;
}
}
let key_down = if is_down { 1 } else { 0 };
Some(format!(
"\u{1b}[{};;{};{};{}_",
vkey, uni, key_down, control_key_state
))
}
pub fn encode_up_down(
&self,
mods: Modifiers,
modes: KeyCodeEncodeModes,
is_down: bool,
) -> Result<String> {
if modes.encoding == KeyboardEncoding::Win32 {
if let Some(res) = self.encode_win32_input_mode(mods, is_down) {
return Ok(res);
}
}
if !is_down {
return Ok(String::new());
}

View File

@ -1,7 +1,9 @@
use ::window::{DeadKeyStatus, KeyCode, KeyEvent, Modifiers, RawKeyEvent, WindowOps};
use anyhow::Context;
use mux::pane::Pane;
use smol::Timer;
use std::rc::Rc;
use termwiz::input::KeyboardEncoding;
pub fn window_mods_to_termwiz_mods(modifiers: ::window::Modifiers) -> termwiz::input::Modifiers {
let mut result = termwiz::input::Modifiers::NONE;
@ -43,6 +45,15 @@ enum OnlyKeyBindings {
}
impl super::TermWindow {
fn encode_win32_input(&self, pane: &Rc<dyn Pane>, key: &KeyEvent) -> Option<String> {
if !self.config.allow_win32_input_mode
|| pane.get_keyboard_encoding() != KeyboardEncoding::Win32
{
return None;
}
key.encode_win32_input_mode()
}
fn process_key(
&mut self,
pane: &Rc<dyn Pane>,
@ -341,7 +352,14 @@ impl super::TermWindow {
log::info!("send to pane key={:?} mods={:?}", key, modifiers);
}
let res = if window_key.key_is_down {
let res = if let Some(encoded) = self.encode_win32_input(&pane, &window_key) {
if self.config.debug_key_events {
log::info!("Encoded input as {:?}", encoded);
}
pane.writer()
.write_all(encoded.as_bytes())
.context("sending win32-input-mode encoded data")
} else if window_key.key_is_down {
pane.key_down(key, modifiers)
} else {
pane.key_up(key, modifiers)

View File

@ -832,8 +832,16 @@ impl Handled {
}
}
impl PartialEq for Handled {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl Eq for Handled {}
/// A key event prior to any dead key or IME composition
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RawKeyEvent {
pub key: KeyCode,
pub modifiers: Modifiers,
@ -843,6 +851,10 @@ pub struct RawKeyEvent {
/// The OS and hardware dependent key code for the key
pub raw_code: u32,
/// The *other* OS and hardware dependent key code for the key
#[cfg(windows)]
pub scan_code: u32,
/// How many times this key repeats
pub repeat_count: u16,
@ -873,6 +885,9 @@ pub struct KeyEvent {
/// If true, this is a key down rather than a key up event
pub key_is_down: bool,
/// If triggered from a raw key event, here it is.
pub raw: Option<RawKeyEvent>,
}
fn normalize_shift(key: KeyCode, modifiers: Modifiers) -> (KeyCode, Modifiers) {
@ -923,6 +938,58 @@ impl KeyEvent {
self
}
#[cfg(not(windows))]
pub fn encode_win32_input_mode(&self) -> Option<String> {
None
}
/// <https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md>
#[cfg(windows)]
pub fn encode_win32_input_mode(&self) -> Option<String> {
let phys = self.raw.as_ref()?;
let vkey = phys.raw_code;
let scan_code = phys.scan_code;
// <https://docs.microsoft.com/en-us/windows/console/key-event-record-str>
// defines the dwControlKeyState values
let mut control_key_state = 0;
const SHIFT_PRESSED: usize = 0x10;
// const RIGHT_ALT_PRESSED: usize = 0x01;
const LEFT_ALT_PRESSED: usize = 0x02;
const LEFT_CTRL_PRESSED: usize = 0x08;
// const RIGHT_CTRL_PRESSED: usize = 0x04;
if self.modifiers.contains(Modifiers::SHIFT) {
control_key_state |= SHIFT_PRESSED;
}
if self.modifiers.contains(Modifiers::ALT) {
control_key_state |= LEFT_ALT_PRESSED;
}
if self.modifiers.contains(Modifiers::CTRL) {
control_key_state |= LEFT_CTRL_PRESSED;
}
let key_down = if self.key_is_down { 1 } else { 0 };
match &self.key {
KeyCode::Composed(_) => None,
KeyCode::Char(c) => {
let uni = *c as u32;
Some(format!(
"\u{1b}[{};{};{};{};{};{}_",
vkey, scan_code, uni, key_down, control_key_state, self.repeat_count
))
}
_ => {
let uni = 0;
Some(format!(
"\u{1b}[{};{};{};{};{};{}_",
vkey, scan_code, uni, key_down, control_key_state, self.repeat_count
))
}
}
}
}
bitflags! {

View File

@ -1451,6 +1451,7 @@ impl WindowView {
modifiers,
repeat_count: 1,
key_is_down: true,
raw: None,
}
.normalize_shift();
@ -1518,6 +1519,7 @@ impl WindowView {
modifiers: Modifiers::NONE,
repeat_count: 1,
key_is_down,
raw: None,
};
inner.ime_text.clear();
@ -1933,7 +1935,7 @@ impl WindowView {
let mut inner = myself.inner.borrow_mut();
inner
.events
.dispatch(WindowEvent::RawKeyEvent(raw_key_event));
.dispatch(WindowEvent::RawKeyEvent(raw_key_event.clone()));
}
if raw_key_handled.is_handled() {
@ -1968,6 +1970,7 @@ impl WindowView {
modifiers: Modifiers::NONE,
repeat_count: 1,
key_is_down,
raw: None,
};
inner.events.dispatch(WindowEvent::KeyEvent(event));
return;
@ -2144,6 +2147,7 @@ impl WindowView {
modifiers,
repeat_count: 1,
key_is_down,
raw: Some(raw_key_event),
}
.normalize_ctrl()
.normalize_shift();

View File

@ -1330,6 +1330,7 @@ unsafe fn ime_composition(
modifiers: Modifiers::NONE,
repeat_count: 1,
key_is_down: true,
raw: None,
};
inner
.events
@ -1760,6 +1761,21 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
keys[VK_RCONTROL as usize] = 0;
}
let handled_raw = Handled::new();
let raw_key_event = RawKeyEvent {
key: match phys_code {
Some(phys) => KeyCode::Physical(phys),
None => KeyCode::RawCode(wparam as _),
},
phys_code,
raw_code: wparam as _,
scan_code: scan_code as _,
modifiers,
repeat_count: 1,
key_is_down: !releasing,
handled: handled_raw.clone(),
};
let key = if msg == WM_IME_CHAR || msg == WM_CHAR {
// If we were sent a character by the IME, some other apps,
// or by ourselves via TranslateMessage, then take that
@ -1770,22 +1786,9 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
// ToUnicode has frustrating statefulness so we take care to
// call it only when we think it will give consistent results.
let handled_raw = Handled::new();
let raw_key_event = RawKeyEvent {
key: match phys_code {
Some(phys) => KeyCode::Physical(phys),
None => KeyCode::RawCode(wparam as _),
},
phys_code,
raw_code: wparam as _,
modifiers,
repeat_count: 1,
key_is_down: !releasing,
handled: handled_raw.clone(),
};
inner
.events
.dispatch(WindowEvent::RawKeyEvent(raw_key_event));
.dispatch(WindowEvent::RawKeyEvent(raw_key_event.clone()));
if handled_raw.is_handled() {
// Cancel any pending dead key
if inner.dead_pending.take().is_some() {
@ -1840,6 +1843,7 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
modifiers,
repeat_count: 1,
key_is_down: !releasing,
raw: None,
}
.normalize_shift()
.normalize_ctrl();
@ -1957,6 +1961,7 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
modifiers,
repeat_count: repeat,
key_is_down: !releasing,
raw: Some(raw_key_event),
}
.normalize_shift();

View File

@ -176,31 +176,30 @@ impl Keyboard {
let raw_modifiers = self.get_key_modifiers();
let xsym = self.state.borrow().key_get_one_sym(xcode);
let handled = Handled::new();
let raw_key_event = RawKeyEvent {
key: match phys_code {
Some(phys) => KeyCode::Physical(phys),
None => KeyCode::RawCode(xcode),
},
phys_code,
raw_code: xcode,
modifiers: raw_modifiers,
repeat_count: 1,
key_is_down: pressed,
handled: handled.clone(),
};
let mut kc = None;
let ksym = if pressed {
let handled = Handled::new();
let raw_key_event = RawKeyEvent {
key: match phys_code {
Some(phys) => KeyCode::Physical(phys),
None => KeyCode::RawCode(xcode),
},
phys_code,
raw_code: xcode,
modifiers: raw_modifiers,
repeat_count: 1,
key_is_down: pressed,
handled: handled.clone(),
};
events.dispatch(WindowEvent::RawKeyEvent(raw_key_event.clone()));
if handled.is_handled() {
self.compose_state.borrow_mut().reset();
log::trace!("raw key was handled; not processing further");
if want_repeat {
return Some(WindowKeyEvent::RawKeyEvent(raw_key_event));
return Some(WindowKeyEvent::RawKeyEvent(raw_key_event.clone()));
}
return None;
}
@ -251,6 +250,7 @@ impl Keyboard {
modifiers: raw_modifiers,
repeat_count: 1,
key_is_down: pressed,
raw: Some(raw_key_event),
}
.normalize_ctrl()
.normalize_shift();

View File

@ -434,6 +434,7 @@ impl XWindowInner {
modifiers: Modifiers::NONE,
repeat_count: 1,
key_is_down: true,
raw: None,
}
.normalize_shift();
self.events.dispatch(WindowEvent::KeyEvent(key_event));