wez / wezterm

A GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust
https://wezfurlong.org/wezterm/
Other
17.71k stars 793 forks source link

Keys entered via Karabiner Elements have strange effects #4001

Closed Cu3PO42 closed 8 months ago

Cu3PO42 commented 1 year ago

What Operating System(s) are you seeing this problem on?

macOS

Which Wayland compositor or X11 Window manager(s) are you using?

I am currently using yabai, but can reproduce the problem without it.

WezTerm version

20230714-110839-b54b0ce4

Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

Describe the bug

I am using the Neo2 keyboard layout. It features multiple layers that can be activated by holding down some modifier keys. On macOS, it is implemented as a combination of a native keyboard layout, as wall as some rules for Karabiner Elements. These rules can be obtained here. It is my understanding that Karabiner captures keyboard events on the internal keyboard, rewrites them and emits new events on a virtual keyboard it adds via a system extension.

When using the above setup to type in any keys from Layer 3 (for example /) into Wezterm, I do not get the key I expect, but rather some shortcut appears to be activated. For example, I ran wezterm --version and subsequently wanted to type ls /, upon entering the /, I instead got ls --versioN (note the capital N at the end). The behaviour of entering the last command from the previous line and switching the capitalisation of the last character appears consistent for any character in that layer that I try to type.

This is in fish. In zsh I observe the following characters appear: ;4~<character> with the character being the character I would get had I not held down the modifier. I use vi keybinding in fish, so the ~ would explain the case toggling.

The keys work as expected in other applications, including Terminal.app, iTerm 2, Kitty and Alacritty. Neo2 also works as expected on Windows, where the setup is also some combination of a native keylayout and a driver for the non-standard layers.

To Reproduce

Install Wezterm on macOS and setup the Neo2 keyboard layout as per the instructions on their website. Since the page is in German, I will summarise the steps here (automatic translations of the page are perfectly readable as well):

  1. Place neo.keylayout and neo.icns in ~/Library/Keyboard Layouts and reboot.
  2. Install Karabiner-Elements (available as a brew cask).
  3. Set up the additional rules by going here and clicking Import.
  4. Activate the keyboard layout.

Finally, the behaviour can be observed by attempting to enter any character from layer 3 in Wezterm. In particular: hold down Caps Lock and press the button labeled as s on a standard QWERTY board. You should obtain a slash if everything were working correctly.

Configuration

The issue is reproducible without any configuration.

Expected Behavior

Entering any particular symbol from my keyboard should have the same effect in WezTerm as it does in other apps.

Logs

I can reproduce the issue without any log output appearing.

Anything else?

No response

wez commented 1 year ago

Please run through https://wezfurlong.org/wezterm/troubleshooting.html#debugging-keyboard-related-issues and share the debug key events output

Cu3PO42 commented 1 year ago

My apologies, I missed that page in the docs. This is the output for me trying to type ls /:

18:55:05.147  INFO   wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: true, handled: Handled(false) }
18:55:05.148  INFO   wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: None }
18:55:05.148  INFO   wezterm_gui::termwindow::keyevent > send to pane DOWN key=Char('l') mods=NONE
18:55:05.148  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "l", Char('l') NONE
18:55:05.219  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: true, handled: Handled(false) }
18:55:05.220  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: None }
18:55:05.220  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('s') mods=NONE
18:55:05.220  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "s", Char('s') NONE
18:55:05.283  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: false, handled: Handled(false) }
18:55:05.283  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
18:55:05.283  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('l') mods=NONE
18:55:05.316  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: false, handled: Handled(false) }
18:55:05.316  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
18:55:05.316  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('s') mods=NONE
18:55:05.318  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: true, handled: Handled(false) }
18:55:05.318  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: None }
18:55:05.318  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char(' ') mods=NONE
18:55:05.318  INFO   wezterm_term::terminalstate::keyboard > key_down: sending " ", Char(' ') NONE
18:55:05.438  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: false, handled: Handled(false) }
18:55:05.439  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
18:55:05.439  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char(' ') mods=NONE
18:55:06.562  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('\u{f72c}'), modifiers: SHIFT | ALT | LEFT_ALT, leds: (empty), phys_code: Some(PageUp), raw_code: 116, repeat_count: 1, key_is_down: true, handled: Handled(false) }
18:55:06.562  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: PageUp, modifiers: SHIFT | ALT | LEFT_ALT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char('\u{f72c}'), modifiers: SHIFT | ALT | LEFT_ALT, leds: (empty), phys_code: Some(PageUp), raw_code: 116, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
18:55:06.563  INFO   wezterm_gui::termwindow::keyevent     > PageUp SHIFT | ALT | LEFT_ALT -> send to pane PageUp SHIFT | ALT | LEFT_ALT
18:55:06.563  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[5;4~", PageUp SHIFT | ALT | LEFT_ALT
18:55:06.563  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('\u{f72c}'), modifiers: SHIFT | ALT | LEFT_ALT, leds: (empty), phys_code: Some(PageUp), raw_code: 116, repeat_count: 1, key_is_down: false, handled: Handled(false) }
18:55:06.563  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: PageUp, modifiers: SHIFT | ALT | LEFT_ALT, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('\u{f72c}'), modifiers: SHIFT | ALT | LEFT_ALT, leds: (empty), phys_code: Some(PageUp), raw_code: 116, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
18:55:06.563  INFO   wezterm_gui::termwindow::keyevent     > PageUp SHIFT | ALT | LEFT_ALT -> send to pane PageUp SHIFT | ALT | LEFT_ALT
18:55:06.576  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), phys_code: Some(S), raw_code: 1, repeat_count: 1, key_is_down: true, handled: Handled(false) }
18:55:06.576  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: None }
18:55:06.576  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('i') mods=NONE
18:55:06.576  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "i", Char('i') NONE
18:55:06.642  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), phys_code: Some(S), raw_code: 1, repeat_count: 1, key_is_down: false, handled: Handled(false) }
18:55:06.642  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), phys_code: Some(S), raw_code: 1, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
18:55:06.642  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('i') mods=NONE

This with a config file containing return { debug_key_events = true }. I had previously tried changing the value of use_ime to no avail. I just tried again and see no difference in behaviour.

In order to type /, I hold down the key labeled as # on a QWERTZ ISO board (I believe this is the same scan code as the key labeled \ on ANSI US) and hit the key labeled s. Via the selected keyboard layout, the s key is translated to i. Through some combination of the native key layout and Karabiner, this key combo is supposed to be remapped to /.

If I turn off Karabiner elements I get the following output, pressing the same buttons as before:

19:05:40.852  INFO   wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: true, handled: Handled(false) }
19:05:40.853  INFO   wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
19:05:40.853  INFO   wezterm_gui::termwindow::keyevent > send to pane DOWN key=Char('l') mods=NONE
19:05:40.853  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "l", Char('l') NONE
19:05:40.935  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: true, handled: Handled(false) }
19:05:40.935  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
19:05:40.935  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('s') mods=NONE
19:05:40.935  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "s", Char('s') NONE
19:05:40.957  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: false, handled: Handled(false) }
19:05:40.957  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('l'), modifiers: NONE, leds: (empty), phys_code: Some(E), raw_code: 14, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
19:05:40.957  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('l') mods=NONE
19:05:40.994  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: true, handled: Handled(false) }
19:05:40.994  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
19:05:40.994  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char(' ') mods=NONE
19:05:40.994  INFO   wezterm_term::terminalstate::keyboard > key_down: sending " ", Char(' ') NONE
19:05:41.035  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: false, handled: Handled(false) }
19:05:41.035  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('s'), modifiers: NONE, leds: (empty), phys_code: Some(H), raw_code: 4, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
19:05:41.035  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('s') mods=NONE
19:05:41.127  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: false, handled: Handled(false) }
19:05:41.127  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char(' '), modifiers: NONE, leds: (empty), phys_code: Some(Space), raw_code: 49, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
19:05:41.127  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char(' ') mods=NONE
19:05:41.267  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(Backslash), modifiers: NONE, leds: (empty), phys_code: Some(Backslash), raw_code: 42, repeat_count: 1, key_is_down: true, handled: Handled(false) }
19:05:41.414  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), phys_code: Some(S), raw_code: 1, repeat_count: 1, key_is_down: true, handled: Handled(false) }
19:05:41.415  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: None }
19:05:41.415  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('i') mods=NONE
19:05:41.415  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "i", Char('i') NONE
19:05:41.506  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), phys_code: Some(S), raw_code: 1, repeat_count: 1, key_is_down: false, handled: Handled(false) }
19:05:41.506  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('i'), modifiers: NONE, leds: (empty), phys_code: Some(S), raw_code: 1, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
19:05:41.506  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('i') mods=NONE
19:05:41.658  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(Backslash), modifiers: NONE, leds: (empty), phys_code: Some(Backslash), raw_code: 42, repeat_count: 1, key_is_down: false, handled: Handled(false) }
lummax commented 1 year ago

For me https://wezfurlong.org/wezterm/config/keyboard-concepts.html#macos-left-and-right-option-key helped.

I have set

local config = {
  send_composed_key_when_left_alt_is_pressed = true,
  send_composed_key_when_right_alt_is_pressed = true,
}

This might break something else I haven't discovered yet though?

Cu3PO42 commented 8 months ago

I was able to figure out what caused this issue. The Karabiner rules for Neo include rules to prevent some special characters from being recognized as shortcuts with option held down. To this end, instances of RALT+Key are rewritten to LSHIFT+LALT+,Key, where the leader acts as some kind of compose key. This corresponds to the keys Wezterm has registered. This is also why the fix by @lummax is effective, because it allows treating the leader as the shortcut that it is.

The offending rules are already deactivated by default in some terminals where they cause problems, such as iTerm. I have submitted a PR upstream to include Wezterm in that list: pqrs-org/KE-complex_modifications#1538.

I don't believe Wezterm is actually behaving faultily here, but rather this is an unfortunate consequence of lots of moving pieces.

github-actions[bot] commented 7 months ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.