fix(windows): preserve Ctrl+/ in ConPTY input records
This commit is contained in:
1 parent
33e7b1d00d
commit
c5622dcff1
2 files changed
+45
-13
No files matched your search
@@ -589,17 +589,45 @@ mod tests {
|
|||||||
/// 当按键已处理、不再 TranslateMessage,中文组字起不来。
|
/// 当按键已处理、不再 TranslateMessage,中文组字起不来。
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[test]
|
#[test]
|
||||||
fn unmodified_letters_stay_on_the_ime_path_in_win32_mode() {
|
fn win32_mode_preserves_text_input_and_modified_slash() {
|
||||||
let mode = TermMode::WIN32_INPUT_MODE;
|
let mode = TermMode::WIN32_INPUT_MODE;
|
||||||
assert_eq!(encode(&keystroke("n"), &mode), None);
|
assert_eq!(encode(&keystroke("n"), &mode), None);
|
||||||
assert_eq!(encode(&keystroke("a"), &mode), None);
|
assert_eq!(encode(&keystroke("a"), &mode), None);
|
||||||
assert_eq!(encode(&keystroke("1"), &mode), None);
|
assert_eq!(encode(&keystroke("1"), &mode), None);
|
||||||
assert_eq!(encode(&keystroke("space"), &mode), None);
|
assert_eq!(encode(&keystroke("space"), &mode), None);
|
||||||
|
assert_eq!(encode(&keystroke("/"), &mode), None);
|
||||||
|
assert_eq!(encode(&keystroke("?"), &mode), None);
|
||||||
|
assert_eq!(encode(&Keystroke::parse("shift-/").unwrap(), &mode), None);
|
||||||
// Ctrl+C 仍走记录,不能为了 IME 把快捷键也放掉。
|
// Ctrl+C 仍走记录,不能为了 IME 把快捷键也放掉。
|
||||||
let mut ctrl_c = keystroke("c");
|
let mut ctrl_c = keystroke("c");
|
||||||
ctrl_c.modifiers.control = true;
|
ctrl_c.modifiers.control = true;
|
||||||
assert!(encode(&ctrl_c, &mode).is_some());
|
assert!(encode(&ctrl_c, &mode).is_some());
|
||||||
assert!(encode(&keystroke("escape"), &mode).is_some());
|
assert!(encode(&keystroke("escape"), &mode).is_some());
|
||||||
|
|
||||||
|
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
|
||||||
|
MAPVK_VK_TO_VSC, MapVirtualKeyW, VkKeyScanW,
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = Keystroke::parse("ctrl-/").unwrap();
|
||||||
|
let mapping = unsafe { VkKeyScanW(b'/' as u16) } as u16;
|
||||||
|
let vk = mapping & 0xff;
|
||||||
|
let scan = unsafe { MapVirtualKeyW(u32::from(vk), MAPVK_VK_TO_VSC) };
|
||||||
|
let modifiers = 8 | ((mapping >> 8 & 1) << 4) | ((mapping >> 8 & 4) >> 1);
|
||||||
|
assert_eq!(
|
||||||
|
encode(&key, &TermMode::WIN32_INPUT_MODE),
|
||||||
|
Some(
|
||||||
|
format!("\x1b[{vk};{scan};0;1;{modifiers};1_\x1b[{vk};{scan};0;0;{modifiers};1_")
|
||||||
|
.into_bytes()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ctrl_slash_keeps_legacy_and_kitty_encodings() {
|
||||||
|
let key = Keystroke::parse("ctrl-/").unwrap();
|
||||||
|
assert_eq!(encode(&key, &TermMode::default()), Some(b"\x1f".to_vec()));
|
||||||
|
let mode = TermMode::WIN32_INPUT_MODE | TermMode::DISAMBIGUATE_ESC_CODES;
|
||||||
|
assert_eq!(encode(&key, &mode), Some(b"\x1b[47;5u".to_vec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 普通空格与字母使用同一文本输入路径:英语布局最终提交 `" "`,IME
|
/// 普通空格与字母使用同一文本输入路径:英语布局最终提交 `" "`,IME
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use gpui::Keystroke;
|
use gpui::Keystroke;
|
||||||
|
|
||||||
/// 无修饰的字母/数字/空格必须交给 IME / `TranslateMessage`,不能编进 PTY。
|
/// 普通可打印字符必须交给 IME / `TranslateMessage`,不能编进 PTY。
|
||||||
///
|
///
|
||||||
/// GPUI 的 Windows 后端:`on_key_down` 一旦 `stop_propagation`,就不会再
|
/// GPUI 的 Windows 后端:`on_key_down` 一旦 `stop_propagation`,就不会再
|
||||||
/// `TranslateMessage`。IME 组字(微软拼音)是 TranslateMessage 喂进去的;
|
/// `TranslateMessage`。IME 组字(微软拼音)是 TranslateMessage 喂进去的;
|
||||||
@@ -18,20 +18,19 @@ pub(super) fn win32_encodes_keystroke(ks: &Keystroke) -> bool {
|
|||||||
}
|
}
|
||||||
let mut chars = key.chars();
|
let mut chars = key.chars();
|
||||||
match (chars.next(), chars.next()) {
|
match (chars.next(), chars.next()) {
|
||||||
(Some(c), None) if c.is_ascii_alphanumeric() => false,
|
(Some(c), None) if c.is_ascii_graphic() => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GPUI 键名 → Win32 虚拟键码。
|
/// GPUI 键名 → Win32 虚拟键码及布局所需的修饰键。
|
||||||
///
|
///
|
||||||
/// 旧壳从 winit fork 的 `RawKeyEventInfo` 直接拿到系统报的 VK;GPUI 的
|
/// 旧壳从 winit fork 的 `RawKeyEventInfo` 直接拿到系统报的 VK;GPUI 的
|
||||||
/// `Keystroke` 只有键名,所以这里按名字反查。表只覆盖**编码器会处理的键**
|
/// `Keystroke` 只有键名,所以这里按名字反查;普通文本仍走 IME 管道。
|
||||||
/// (控制键、方向、功能键)——可打印字符在 GPUI 走 IME 管道,不经这里。
|
pub(super) fn virtual_key_of(key: &str) -> Option<(u16, u16)> {
|
||||||
pub(super) fn virtual_key_of(key: &str) -> Option<u16> {
|
|
||||||
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
|
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
|
||||||
VK_BACK, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_HOME, VK_INSERT, VK_LEFT,
|
VK_BACK, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_HOME, VK_INSERT, VK_LEFT,
|
||||||
VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SPACE, VK_TAB, VK_UP,
|
VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SPACE, VK_TAB, VK_UP, VkKeyScanW,
|
||||||
};
|
};
|
||||||
|
|
||||||
let vk = match key {
|
let vk = match key {
|
||||||
@@ -50,6 +49,11 @@ pub(super) fn virtual_key_of(key: &str) -> Option<u16> {
|
|||||||
"delete" => VK_DELETE,
|
"delete" => VK_DELETE,
|
||||||
"pageup" => VK_PRIOR,
|
"pageup" => VK_PRIOR,
|
||||||
"pagedown" => VK_NEXT,
|
"pagedown" => VK_NEXT,
|
||||||
|
"/" => {
|
||||||
|
// GPUI 将布局所需的 Shift 合入键名,不能写死美式布局的 VK_OEM_2。
|
||||||
|
let mapping = unsafe { VkKeyScanW(b'/' as u16) };
|
||||||
|
return (mapping != -1).then_some((mapping as u16 & 0xff, mapping as u16 >> 8));
|
||||||
|
},
|
||||||
// F1..F24 在 VK 表里连号。
|
// F1..F24 在 VK 表里连号。
|
||||||
key if key.starts_with('f') => {
|
key if key.starts_with('f') => {
|
||||||
let index: u16 = key[1..].parse().ok()?;
|
let index: u16 = key[1..].parse().ok()?;
|
||||||
@@ -68,7 +72,7 @@ pub(super) fn virtual_key_of(key: &str) -> Option<u16> {
|
|||||||
c.to_ascii_uppercase() as u16
|
c.to_ascii_uppercase() as u16
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Some(vk)
|
Some((vk, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 控制键必须携带真实 `KEY_EVENT_RECORD` 的字符值(Esc=0x1B、Enter=0x0D、
|
/// 控制键必须携带真实 `KEY_EVENT_RECORD` 的字符值(Esc=0x1B、Enter=0x0D、
|
||||||
@@ -121,17 +125,17 @@ pub(super) fn win32_input_record(ks: &Keystroke, key_down: bool) -> Option<Vec<u
|
|||||||
const LEFT_ALT_PRESSED: u32 = 0x0002;
|
const LEFT_ALT_PRESSED: u32 = 0x0002;
|
||||||
const LEFT_CTRL_PRESSED: u32 = 0x0008;
|
const LEFT_CTRL_PRESSED: u32 = 0x0008;
|
||||||
|
|
||||||
let vk = virtual_key_of(ks.key.as_str())?;
|
let (vk, layout_modifiers) = virtual_key_of(ks.key.as_str())?;
|
||||||
// 扫描码问系统要,不硬编码:非 US 布局与笔记本键盘上这张表并不通用。
|
// 扫描码问系统要,不硬编码:非 US 布局与笔记本键盘上这张表并不通用。
|
||||||
let scan_code = unsafe { MapVirtualKeyW(u32::from(vk), MAPVK_VK_TO_VSC) };
|
let scan_code = unsafe { MapVirtualKeyW(u32::from(vk), MAPVK_VK_TO_VSC) };
|
||||||
let mut control_key_state = 0u32;
|
let mut control_key_state = 0u32;
|
||||||
if ks.modifiers.shift {
|
if ks.modifiers.shift || layout_modifiers & 1 != 0 {
|
||||||
control_key_state |= SHIFT_PRESSED;
|
control_key_state |= SHIFT_PRESSED;
|
||||||
}
|
}
|
||||||
if ks.modifiers.alt {
|
if ks.modifiers.alt || layout_modifiers & 4 != 0 {
|
||||||
control_key_state |= LEFT_ALT_PRESSED;
|
control_key_state |= LEFT_ALT_PRESSED;
|
||||||
}
|
}
|
||||||
if ks.modifiers.control {
|
if ks.modifiers.control || layout_modifiers & 2 != 0 {
|
||||||
control_key_state |= LEFT_CTRL_PRESSED;
|
control_key_state |= LEFT_CTRL_PRESSED;
|
||||||
}
|
}
|
||||||
let key_down = u8::from(key_down);
|
let key_down = u8::from(key_down);
|
||||||
|
|||||||
Reference in new issue
Block a user