mirror of
https://github.com/wez/wezterm.git
synced 2024-09-19 02:37:51 +03:00
x11/wayland: improve non-latin ctrl/alt keypresses
For eg: RU layout, CTRL-S shouldn't result in ы in the context of a terminal. The approach taken here is similar to kitty; when the key combination doesn't produce a definitive composed output, and when any of ctrl/alt/super are present, we treat the keypress as though it were the same as the one from the system default keymap. The result is that ctrl-c now works like ctrl-c and alt-b and alt-f work like their latin counterparts. Hopefully there are no downsides to this! refs: https://github.com/wez/wezterm/issues/2845 refs: https://github.com/kovidgoyal/kitty/issues/606
This commit is contained in:
parent
db720bd78c
commit
21e19ca091
@ -62,6 +62,7 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* Windows: inconsistencies with win32 input mode. Thanks to @kreudom! #2235
|
||||
* macOS: font size is zoomed or window appears empty when first launched on a
|
||||
secondary monitor with different scaling settings from the primary monitor. #3503
|
||||
* X11/Wayland: CTRL/ALT didn't work as expected for non-latin keyboard layouts. #2845
|
||||
|
||||
### 20230408-112425-69ae8472
|
||||
|
||||
|
@ -16,9 +16,11 @@ use xkbcommon::xkb;
|
||||
pub struct Keyboard {
|
||||
context: xkb::Context,
|
||||
keymap: RefCell<xkb::Keymap>,
|
||||
_default_keymap: RefCell<xkb::Keymap>,
|
||||
device_id: i32,
|
||||
|
||||
state: RefCell<xkb::State>,
|
||||
default_state: RefCell<xkb::State>,
|
||||
compose_state: RefCell<Compose>,
|
||||
phys_code_map: RefCell<HashMap<xkb::Keycode, PhysKeyCode>>,
|
||||
mods_leds: RefCell<(Modifiers, KeyboardLedStatus)>,
|
||||
@ -109,17 +111,7 @@ impl Compose {
|
||||
}
|
||||
ComposeStatus::Nothing => {
|
||||
let utf8 = key_state.borrow().key_get_utf8(xcode);
|
||||
// CTRL-<ALPHA> is helpfully encoded in the form that we would
|
||||
// send to the terminal, however, we do want the chance to
|
||||
// distinguish between eg: CTRL-i and Tab, so if we ended up
|
||||
// with a control code representation from the xkeyboard layer,
|
||||
// discard it.
|
||||
// <https://github.com/wez/wezterm/issues/1851>
|
||||
if utf8.len() == 1 && utf8.as_bytes()[0] < 0x20 {
|
||||
FeedResult::Nothing(String::new(), xsym)
|
||||
} else {
|
||||
FeedResult::Nothing(utf8, xsym)
|
||||
}
|
||||
FeedResult::Nothing(utf8, xsym)
|
||||
}
|
||||
ComposeStatus::Cancelled => {
|
||||
self.state.reset();
|
||||
@ -129,6 +121,27 @@ impl Compose {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_keymap(context: &xkb::Context) -> Option<xkb::Keymap> {
|
||||
// use $XKB_DEFAULT_RULES or system default
|
||||
let system_default_rules = "";
|
||||
// use $XKB_DEFAULT_MODEL or system default
|
||||
let system_default_model = "";
|
||||
// use $XKB_DEFAULT_LAYOUT or system default
|
||||
let system_default_layout = "";
|
||||
// use $XKB_DEFAULT_VARIANT or system default
|
||||
let system_default_variant = "";
|
||||
|
||||
xkb::Keymap::new_from_names(
|
||||
context,
|
||||
system_default_rules,
|
||||
system_default_model,
|
||||
system_default_layout,
|
||||
system_default_variant,
|
||||
None,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new_from_string(s: String) -> anyhow::Result<Self> {
|
||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||
@ -150,11 +163,17 @@ impl Keyboard {
|
||||
|
||||
let phys_code_map = build_physkeycode_map(&keymap);
|
||||
|
||||
let default_keymap = default_keymap(&context)
|
||||
.ok_or_else(|| anyhow!("Failed to load system default keymap"))?;
|
||||
let default_state = xkb::State::new(&default_keymap);
|
||||
|
||||
Ok(Self {
|
||||
context,
|
||||
device_id: -1,
|
||||
keymap: RefCell::new(keymap),
|
||||
state: RefCell::new(state),
|
||||
_default_keymap: RefCell::new(default_keymap),
|
||||
default_state: RefCell::new(default_state),
|
||||
compose_state: RefCell::new(Compose {
|
||||
state: compose_state,
|
||||
composition: String::new(),
|
||||
@ -215,11 +234,18 @@ impl Keyboard {
|
||||
}
|
||||
|
||||
let phys_code_map = build_physkeycode_map(&keymap);
|
||||
|
||||
let default_keymap = default_keymap(&context)
|
||||
.ok_or_else(|| anyhow!("Failed to load system default keymap"))?;
|
||||
let default_state = xkb::State::new(&default_keymap);
|
||||
|
||||
let kbd = Keyboard {
|
||||
context,
|
||||
device_id,
|
||||
keymap: RefCell::new(keymap),
|
||||
state: RefCell::new(state),
|
||||
_default_keymap: RefCell::new(default_keymap),
|
||||
default_state: RefCell::new(default_state),
|
||||
compose_state: RefCell::new(Compose {
|
||||
state: compose_state,
|
||||
composition: String::new(),
|
||||
@ -331,14 +357,45 @@ impl Keyboard {
|
||||
sym
|
||||
}
|
||||
FeedResult::Nothing(utf8, sym) => {
|
||||
if !utf8.is_empty() {
|
||||
// Composition had no special expansion.
|
||||
// Xkb will return a textual representation of the key even when
|
||||
// it is not generally useful; for example, when CTRL, ALT or SUPER
|
||||
// are held, we don't want its mapping as it can be counterproductive:
|
||||
// CTRL-<ALPHA> is helpfully encoded in the form that we would
|
||||
// send to the terminal, however, we do want the chance to
|
||||
// distinguish between eg: CTRL-i and Tab.
|
||||
//
|
||||
// This logic excludes that textual expansion for this situation.
|
||||
//
|
||||
// <https://github.com/wez/wezterm/issues/1851>
|
||||
// <https://github.com/wez/wezterm/issues/2845>
|
||||
if !utf8.is_empty()
|
||||
&& !raw_modifiers
|
||||
.intersects(Modifiers::CTRL | Modifiers::ALT | Modifiers::SUPER)
|
||||
{
|
||||
kc.replace(crate::KeyCode::composed(&utf8));
|
||||
}
|
||||
|
||||
// If we don't have a textual expansion in this case, we will
|
||||
// consider the equivalent key from the system default / base
|
||||
// layout.
|
||||
// For example, if RU is active and they pressed CTRL-S that
|
||||
// will produce utf8=ы here, which is not useful.
|
||||
// Looking up in the default keymap will resolve us to the S
|
||||
// key which is more desirable in the context of a terminal.
|
||||
// default_xsym is that base key
|
||||
let default_xsym = self.default_state.borrow().key_get_one_sym(xcode);
|
||||
|
||||
log::trace!(
|
||||
"process_key_event: RawKeyEvent FeedResult::Nothing: \
|
||||
{utf8:?}, {sym:?}. kc -> {kc:?}"
|
||||
{utf8:?}, {sym:?}. kc -> {kc:?} def_sym={default_xsym:?}"
|
||||
);
|
||||
sym
|
||||
if kc.is_none() {
|
||||
// Use the default key layout symbol instead
|
||||
default_xsym
|
||||
} else {
|
||||
sym
|
||||
}
|
||||
}
|
||||
FeedResult::Cancelled => {
|
||||
log::trace!("process_key_event: RawKeyEvent FeedResult::Cancelled");
|
||||
|
Loading…
Reference in New Issue
Block a user