1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-27 02:25:28 +03:00

Fix win32 input mode encoding inconsistencies

This pulls in almost all of the original PR in #2235.
I skipped a dead key case that I recall being tricky:
I didn't want to break the non win32-input mode version
of that.

I'd be happy to have that case re-evaluated in a smaller
PR where we can focus on its details.

Co-authored-by: Dominik Kreutzer <kreudom@gmail.com>
This commit is contained in:
Wez Furlong 2023-04-16 13:07:18 -07:00
parent 7aa611f4cb
commit 3f820b93d9
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
3 changed files with 191 additions and 93 deletions

View File

@ -43,6 +43,7 @@ As features stabilize some brief notes about them will accumulate here.
integrated titlebar buttons. #3499
* Windows: closing a window while the debug overlay was active could
leave a lingering wezterm-gui.exe running. #3522
* Windows: inconsistencies with win32 input mode. Thanks to @kreudom! #2235
### 20230408-112425-69ae8472

View File

@ -1375,6 +1375,9 @@ pub struct KeyEvent {
/// If triggered from a raw key event, here it is.
pub raw: Option<RawKeyEvent>,
#[cfg(windows)]
pub win32_uni_char: Option<char>,
}
fn normalize_shift(key: KeyCode, modifiers: Modifiers) -> (KeyCode, Modifiers) {
@ -1536,7 +1539,10 @@ impl KeyEvent {
const LEFT_CTRL_PRESSED: usize = 0x08;
const RIGHT_CTRL_PRESSED: usize = 0x04;
if self.modifiers.contains(Modifiers::SHIFT) {
if self
.modifiers
.intersects(Modifiers::SHIFT | Modifiers::LEFT_SHIFT | Modifiers::RIGHT_SHIFT)
{
control_key_state |= SHIFT_PRESSED;
}
@ -1565,36 +1571,7 @@ impl KeyEvent {
match &self.key {
KeyCode::Composed(_) => None,
KeyCode::Char(c) => {
let c = match *c {
// Delete key is transmitted as 0x0
'\x7f' => '\x00',
// Backspace key is transmitted as 0x8, 0x7f or 0x0
'\x08' => {
if self.modifiers.contains(Modifiers::CTRL) {
if self.modifiers.contains(Modifiers::ALT)
|| self.modifiers.contains(Modifiers::SHIFT)
{
'\x00'
} else {
'\x7f'
}
} else {
'\x08'
}
}
_ => *c,
};
let c = if self.modifiers.contains(Modifiers::CTRL) {
// Ensure that we rewrite the unicode value to the ASCII CTRL
// equivalent value.
// <https://github.com/microsoft/terminal/issues/13134>
ctrl_mapping(c).unwrap_or(c)
} else {
c
};
let uni = c as u32;
let uni = self.win32_uni_char.unwrap_or(*c) as u32;
Some(format!(
"\u{1b}[{};{};{};{};{};{}_",
vkey, scan_code, uni, key_down, control_key_state, self.repeat_count
@ -2176,7 +2153,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"o".to_string()
@ -2188,7 +2167,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[111;1:3u".to_string()
@ -2209,7 +2190,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[11;1~".to_string()
@ -2221,7 +2204,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[11;1:3~".to_string()
@ -2239,7 +2224,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;4u".to_string()
@ -2251,7 +2238,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;4u".to_string()
@ -2264,7 +2253,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[49;4u".to_string()
@ -2278,7 +2269,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::K1)
)
@ -2293,7 +2286,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;6u".to_string()
@ -2305,7 +2300,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;6u".to_string()
@ -2330,6 +2327,8 @@ mod test {
scan_code: 0,
repeat_count: 1,
}),
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;6u".to_string()
@ -2342,7 +2341,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;8u".to_string()
@ -2354,7 +2355,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\x1b[105;8u".to_string()
@ -2375,7 +2378,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\u{1b}[97:65;1u".to_string()
@ -2387,7 +2392,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\u{1b}[97:65;1:3u".to_string()
@ -2431,7 +2438,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2446,7 +2455,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2461,7 +2472,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2476,7 +2489,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2500,7 +2515,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2515,7 +2532,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2531,7 +2550,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2546,7 +2567,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
None
)
@ -2562,7 +2585,9 @@ mod test {
leds: KeyboardLedStatus::NUM_LOCK,
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad0)
)
@ -2577,7 +2602,9 @@ mod test {
leds: KeyboardLedStatus::NUM_LOCK,
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad0)
)
@ -2593,7 +2620,9 @@ mod test {
leds: KeyboardLedStatus::NUM_LOCK,
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad5)
)
@ -2609,7 +2638,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad5)
)
@ -2634,7 +2665,9 @@ mod test {
leds: KeyboardLedStatus::NUM_LOCK,
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad5)
)
@ -2649,7 +2682,9 @@ mod test {
leds: KeyboardLedStatus::NUM_LOCK,
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad5)
)
@ -2665,7 +2700,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad5)
)
@ -2681,7 +2718,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: false,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::Keypad5)
)
@ -2701,7 +2740,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\"".to_string()
@ -2714,7 +2755,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"\"".to_string()
@ -2727,7 +2770,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"!".to_string()
@ -2740,7 +2785,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
"".to_string()
@ -2762,7 +2809,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::A)
)
@ -2778,7 +2827,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::A)
)
@ -2803,7 +2854,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::A)
)
@ -2819,7 +2872,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::A)
)
@ -2839,7 +2894,9 @@ mod test {
leds: KeyboardLedStatus::NUM_LOCK,
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
" ".to_string()
@ -2852,7 +2909,9 @@ mod test {
leds: KeyboardLedStatus::CAPS_LOCK,
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
}
.encode_kitty(flags),
" ".to_string()
@ -2866,7 +2925,9 @@ mod test {
leds: KeyboardLedStatus::empty(),
repeat_count: 1,
key_is_down: true,
raw: None
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::NumLock)
)
@ -2883,6 +2944,8 @@ mod test {
repeat_count: 1,
key_is_down: true,
raw: None,
#[cfg(windows)]
win32_uni_char: None,
},
Some(PhysKeyCode::CapsLock)
)

View File

@ -1964,6 +1964,7 @@ unsafe fn ime_composition(
repeat_count: 1,
key_is_down: true,
raw: None,
win32_uni_char: None,
};
inner
.events
@ -2349,8 +2350,11 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
return Some(0);
}
let keys = {
let mut keys = [0u8; 256];
GetKeyboardState(keys.as_mut_ptr());
keys
};
let mut modifiers = Modifiers::default();
if keys[VK_SHIFT as usize] & 0x80 != 0 {
@ -2410,18 +2414,6 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
modifiers |= Modifiers::SUPER;
}
// If control is pressed, clear that out and remember it in our
// own set of modifiers.
// We used to also remove shift from this set, but it impacts
// handling of eg: ctrl+shift+' (which is equivalent to ctrl+" in a US English
// layout.
// The shift normalization is now handled by the normalize_shift() method.
if modifiers.contains(Modifiers::CTRL) {
keys[VK_CONTROL as usize] = 0;
keys[VK_LCONTROL as usize] = 0;
keys[VK_RCONTROL as usize] = 0;
}
let mut leds = KeyboardLedStatus::empty();
if keys[VK_CAPITAL as usize] & 1 != 0 {
leds |= KeyboardLedStatus::CAPS_LOCK;
@ -2446,11 +2438,14 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
handled: handled_raw.clone(),
};
let key = if msg == WM_IME_CHAR || msg == WM_CHAR {
let (key, win32_uni_char) = 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
// value as-is.
Some(KeyCode::Char(std::char::from_u32_unchecked(wparam as u32)))
(
Some(KeyCode::Char(std::char::from_u32_unchecked(wparam as u32))),
None,
)
} else {
// Otherwise we're dealing with a raw key message.
// ToUnicode has frustrating statefulness so we take care to
@ -2475,7 +2470,8 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
// If this event is only modifiers then don't ask the system
// for further resolution, as we don't want ToUnicode to
// perturb its inscrutable global state.
phys_code.map(|p| p.to_key_code())
// Modifier-only keypresses are reported as NUL when using win32 input mode.
(phys_code.map(|p| p.to_key_code()), Some('\x00'))
} else {
// If we think this might be a dead key, process it for ourselves.
// Our KeyboardLayoutInfo struct probed the layout for the key
@ -2514,7 +2510,11 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
leds,
repeat_count: 1,
key_is_down: !releasing,
raw: None,
win32_uni_char: Some(c),
raw: Some(RawKeyEvent {
scan_code: 0,
..raw_key_event.clone()
}),
}
.normalize_shift()
.resurface_positional_modifier_key()
@ -2580,7 +2580,7 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
};
if dead.is_some() {
dead
(dead, None)
} else {
// We get here for the various UP (but not DOWN as we shortcircuit
// those above) messages.
@ -2588,6 +2588,8 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
// rather than calling TranslateMessage to do it for us,
// so that we have tighter control over the key processing.
let mut out = [0u16; 16];
let win32_uni_char = {
let res = ToUnicode(
wparam as u32,
scan_code as u32,
@ -2598,6 +2600,35 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
);
match res {
1 => Some(std::char::from_u32_unchecked(out[0] as u32)),
0 => Some('\x00'),
_ => None,
}
};
let mut keys = keys;
// If control is pressed, clear that out and remember it in our
// own set of modifiers.
// We used to also remove shift from this set, but it impacts
// handling of eg: ctrl+shift+' (which is equivalent to ctrl+" in a US English
// layout.
// The shift normalization is now handled by the normalize_shift() method.
if modifiers.contains(Modifiers::CTRL) {
keys[VK_CONTROL as usize] = 0;
keys[VK_LCONTROL as usize] = 0;
keys[VK_RCONTROL as usize] = 0;
}
let res = ToUnicode(
wparam as u32,
scan_code as u32,
keys.as_ptr(),
out.as_mut_ptr(),
out.len() as i32,
0,
);
let key = match res {
1 => Some(KeyCode::Char(std::char::from_u32_unchecked(out[0] as u32))),
// No mapping, so use our raw info
0 => {
@ -2628,7 +2659,9 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
KeyboardLayoutInfo::clear_key_state();
None
}
}
};
(key, win32_uni_char)
}
}
};
@ -2644,6 +2677,7 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
leds,
repeat_count: repeat,
key_is_down: !releasing,
win32_uni_char,
raw: Some(raw_key_event),
}
.normalize_shift();