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:
parent
4efcc81bee
commit
4524abcdba
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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! {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user