wez / wezterm

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

Keyevent misinterpreted on Shift + key on MacOS when inside terminal Emacs #3533

Open gagbo opened 1 year ago

gagbo 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?

No response

WezTerm version

wezterm 20230408-112425-69ae8472

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 have the same issue when using Emacs in a terminal window (emacs -q -nw).

I actually use an alternate, custom layout on MacOS that maps the numbers to shift+number (within other changes that shouldn't matter).

When I hit "Shift + =" (that's supposed to be 8), I expect Emacs to see 8 and treat that for the next command. Instead, I see ∼ is undefined. Here is the log of the keyevents:

11:19:16.108  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('8'), modifiers: SHIFT, phys_code: Some(K8), raw_code: 28, repeat_count: 1, key_is_down: true, handled: Handled(false) }
11:19:16.108  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('8'), modifiers: SHIFT, repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char('8'), modifiers: SHIFT, phys_code: Some(K8), raw_code: 28, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
11:19:16.108  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('8') mods=SHIFT
11:19:16.108  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;2;56~", Char('8') SHIFT
11:19:16.162  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('8'), modifiers: SHIFT, phys_code: Some(K8), raw_code: 28, repeat_count: 1, key_is_down: false, handled: Handled(false) }
11:19:16.162  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('8'), modifiers: SHIFT, repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('8'), modifiers: SHIFT, phys_code: Some(K8), raw_code: 28, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
11:19:16.162  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('8') mods=SHIFT

Notes

Narrow reproducing surface

Note that I can't reproduce that bug in most other setups:

In the basic wezterm window, the event looks like that (changed "8" to "9" just because)

11:29:25.762  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('9'), modifiers: SHIFT, phys_code: Some(K9), raw_code: 25, repeat_count: 1, key_is_down: true, handled: Handled(false) }
11:29:25.762  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('9'), modifiers: SHIFT, repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char('9'), modifiers: SHIFT, phys_code: Some(K9), raw_code: 25, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
11:29:25.762  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('9') mods=SHIFT
11:29:25.762  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "9", Char('9') SHIFT
11:29:25.850  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('9'), modifiers: SHIFT, phys_code: Some(K9), raw_code: 25, repeat_count: 1, key_is_down: false, handled: Handled(false) }
11:29:25.850  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('9'), modifiers: SHIFT, repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Char('9'), modifiers: SHIFT, phys_code: Some(K9), raw_code: 25, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
11:29:25.850  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('9') mods=SHIFT

So there are probably specifics around how Emacs handles the events that the terminal sends to it (and probably the same with neovim built-in terminal), but there might be a trick that works for all of those, that other terminal figured out.

TERM variable and terminfo entries

Note: I didn't try to change the TERM variable, I assumed that the brew install both added a terminfo entry and set the TERM for Wezterm to a matching value.

Custom firmware

I use a custom firmware for my keyboard based on QMK, but I don't think this matters because the keycodes I use to send "8" or "9" are very vanilla (no tap-dance/mod-tap involved)

To Reproduce

No response

Configuration

no config

Expected Behavior

No response

Logs

No response

Anything else?

Extracted from #3124

wez commented 1 year ago

Does emacs in xterm behave the same way? You can use XQuartz to get and run xterm to test that on your mac: https://www.xquartz.org/

gagbo commented 1 year ago

I can't switch easily layouts while in XTerm inside XQuartz, but I know I don't have the issue with Emacs in iTerm or Kitty or Terminal.app. Why XTerm in particular?

wez commented 1 year ago

xterm defines and specifies the modifyOtherKeys keyboard encoding scheme that is being enabled by your editor.

wezterm aims to follow what xterm does. There are other terminals that partially implement support for that specification, and other editors that follow those partial implementations (https://github.com/wez/wezterm/issues/3180#issuecomment-1498381614).

For the issue you reported:

If emacs works in xterm but not in wezterm then it is a wezterm issue. If emacs doesn't work in xterm or wezterm then it is an emacs issue.

gagbo commented 1 year ago

I just tested on XTerm (the trick was to start XQuartz with the alternative layout active) and Emacs behaves "normally" there (that means just as iTerm, Terminal.app, and others)

wez commented 1 year ago

I can't reproduce this, perhaps because I have no idea what kind of custom keyboard layout situation you have going on. Looking at the logs in the original post, it appears as though your keyboard is generating SHIFT-8 instead of just the 8 that you want. I would suggest trying to debug why that is and whether you can prevent it from sending the SHIFT modifier in that case.

gagbo commented 1 year ago

The layout doesn't seem to do anything too weird.

The actual file is huge (6225 lines). but the relavant parts here are:

The keymap index is 1 because of me holding only a shift key:

<modifierMap id="commonModifiers" defaultIndex="0">
        <keyMapSelect mapIndex="0">
            <modifier keys=""/>
        </keyMapSelect>
        <keyMapSelect mapIndex="1">
            <modifier keys="anyShift"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="2">
            <modifier keys="caps"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="3">
            <modifier keys="anyShift caps"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="4">
            <modifier keys="anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="5">
            <modifier keys="anyShift anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="6">
            <modifier keys="anyShift caps anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="7">
            <modifier keys="caps anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="8">
            <modifier keys="anyControl anyOption anyShift caps?"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="9">
            <modifier keys="command caps? anyOption? anyControl?"/>
            <modifier keys="anyControl caps? anyOption?"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="10">
            <modifier keys="anyShift command caps? anyOption? anyControl?"/>
            <modifier keys="anyShift anyControl caps?"/>
        </keyMapSelect>
    </modifierMap>
    <modifierMap id="commonModifiers" defaultIndex="0">
        <keyMapSelect mapIndex="0">
            <modifier keys=""/>
        </keyMapSelect>
        <keyMapSelect mapIndex="1">
            <modifier keys="anyShift"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="2">
            <modifier keys="caps"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="3">
            <modifier keys="anyShift caps"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="4">
            <modifier keys="anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="5">
            <modifier keys="anyShift anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="6">
            <modifier keys="anyShift caps anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="7">
            <modifier keys="caps anyOption"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="8">
            <modifier keys="anyControl anyOption anyShift caps?"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="9">
            <modifier keys="command caps? anyOption? anyControl?"/>
            <modifier keys="anyControl caps? anyOption?"/>
        </keyMapSelect>
        <keyMapSelect mapIndex="10">
            <modifier keys="anyShift command caps? anyOption? anyControl?"/>
            <modifier keys="anyShift anyControl caps?"/>
        </keyMapSelect>
    </modifierMap>

Then the keycode for the 8 physical key triggers an action on that index 1 layer:

<keyMapSet id="ISO">
...
        <keyMap index="1">
        ...
                    <key code="28" action="8"/>

And then that action is just mapped to the output 8 when there's no dead key shenanigans

<action id="8">
            <when state="none" output="8"/>
            <when state="barre" output="≠"/>
            <when state="cercle" output="⊜"/>
            <when state="cir+" next="cir+" output="⁼"/>
            <when state="cir-" next="cir-" output="₌"/>
            <when state="circonflexe" output="₈"/>
            <when state="exposant" output="⁼"/>
            <when state="indices" output="₌"/>
            <when state="grec2" next="grec2" output="8"/>
            <when state="latin" output="⁜"/>
            <when state="rond" output="❽"/>
            <when state="scientifique" output="≅"/>
            <when state="scientifiquebarre" output="≆"/>
        </action>

My knowledge in .keylayout files is limited, but I don't feel like it's doing something weird. The difference with other layouts probably is the additional action indirection, but that shouldn't be something visible in the terminal right?

wez commented 1 year ago

I don't know anything about how the keyboard layout stuff you shared works, all I can say is that when you press 8, you're sending SHIFT+8 based on your original logs:

11:19:16.108  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Char('8'), modifiers: SHIFT, phys_code: Some(K8), raw_code: 28, repeat_count: 1, key_is_down: true, handled: Handled(false) }
11:19:16.108  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('8'), modifiers: SHIFT, repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Char('8'), modifiers: SHIFT, phys_code: Some(K8), raw_code: 28, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
11:19:16.108  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('8') mods=SHIFT
11:19:16.108  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;2;56~", Char('8') SHIFT

if you can get it to not send SHIFT then I think this issue will be resolved.

gagbo commented 1 year ago

I don't think it's really possible to have the layout working differently here. The main part that I shared about the layout is that "Shift + 1C" (28) is not directly mapped to an output like in most OS layouts.

Instead Shift 1C is mapped in the OS layout to an action that in turn (when there's no dead key active; state == none) will have the output 8. I suppose this extra OS indirection somehow leaks in the key_event processing that wezterm receives and that confuses the program a bit. I don't know why this only manifests in wezterm though

mrozekma commented 1 year ago

I think I'm having the same problem. This is the debug_key_events output when I press Shift+a in emacs, expecting to type a capital A but instead getting 65~:

02:35:01.682  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(LeftShift), modifiers: SHIFT | LEFT_SHIFT, leds:
(empty), phys_code: Some(LeftShift), raw_code: 16, scan_code: 42, repeat_count: 1, key_is_down: true, handled: Handled(false) }
02:35:01.684  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: LeftShift, modifiers: SHIFT | LEFT_SHIFT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: SHIFT | LEFT_SHIFT, leds: (empty), phys_code: Some(LeftShift), raw_code: 16, scan_code: 42, repeat_count: 1, key_is_down: true, handled: Handled(false) }), win32_uni_char: Some('\0') }
02:35:01.686  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=LeftShift mods=SHIFT | LEFT_SHIFT
02:35:02.072  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(A), modifiers: SHIFT | LEFT_SHIFT, leds: (empty), phys_code: Some(A), raw_code: 65, scan_code: 30, repeat_count: 1, key_is_down: true, handled: Handled(false) }
02:35:02.074  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('A'), modifiers: LEFT_SHIFT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(A), modifiers: SHIFT | LEFT_SHIFT, leds: (empty), phys_code: Some(A), raw_code: 65, scan_code: 30, repeat_count: 1, key_is_down: true, handled: Handled(false) }), win32_uni_char: Some('A') }
02:35:02.076  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char('A') mods=LEFT_SHIFT
02:35:02.078  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;1;65~", Char('A') LEFT_SHIFT
02:35:02.207  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 16, scan_code: 42, repeat_count: 1, key_is_down: false, handled: Handled(false) }
02:35:02.268  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: LeftShift, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 16, scan_code: 42, repeat_count: 1, key_is_down: false, handled: Handled(false) }), win32_uni_char: Some('\0') }
02:35:02.273  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=LeftShift mods=NONE
02:35:02.366  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(A), modifiers: NONE, leds: (empty), phys_code: Some(A), raw_code: 65, scan_code: 30, repeat_count: 1, key_is_down: false, handled: Handled(false) }
02:35:02.432  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char('a'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(A), modifiers: NONE, leds: (empty), phys_code: Some(A), raw_code: 65, scan_code: 30, repeat_count: 1, key_is_down: false, handled: Handled(false) }), win32_uni_char: Some('a') }
02:35:02.438  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char('a') mods=NONE

I only get this behavior in Emacs, and as far as I know I don't have any strange keyboard configuration or bindings.

simao commented 1 year ago

I have the same problem in emacs, with SHIFT-SPACE. (SHIFT-A) works as expected.

I can't even run describe-key in emacs, it just prints 2~, instead of space.

~ ❯ xxd
 00000000: 20

Seems correct though, but in emacs it doesn't work? As the other users, with other terminals it works, including xterm, rxvt, kitty.

In the logs I see:

12:25:22.596  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }
12:25:22.597  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: LeftShift, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
12:25:22.597  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=LeftShift mods=NONE
12:25:23.264  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(Space), modifiers: SHIFT, leds: (empty), phys_code: Some(Space), raw_code: 65, repeat_count: 1, key_is_down: true, handled: Handled(false) }
12:25:23.264  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(Space), modifiers: SHIFT, leds: (empty), phys_code: Some(Space), raw_code: 65, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
12:25:23.264  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char(' ') mods=SHIFT
12:25:23.264  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;2;32~", Char(' ') SHIFT
12:25:23.384  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(Space), modifiers: SHIFT, leds: (empty), phys_code: Some(Space), raw_code: 65, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
12:25:23.384  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char(' ') mods=SHIFT
12:25:24.315  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: LeftShift, modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: SHIFT, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
12:25:24.315  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=LeftShift mods=SHIFT
12:25:23.264  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;2;32~", Char(' ') SHIFT

Does not seem correct?

Stranger still, outside emacs, I get:

12:29:01.156  INFO   wezterm_term::terminalstate::keyboard > key_down: sending " ", Char(' ') SHIFT

:confused:

jzxu commented 1 year ago

I have the same problem in emacs, with SHIFT-SPACE. (SHIFT-A) works as expected.

I can't even run describe-key in emacs, it just prints 2~, instead of space.

~ ❯ xxd
 00000000: 20

Seems correct though, but in emacs it doesn't work? As the other users, with other terminals it works, including xterm, rxvt, kitty.

In the logs I see:

12:25:22.596  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }
12:25:22.597  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: LeftShift, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
12:25:22.597  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=LeftShift mods=NONE
12:25:23.264  INFO   wezterm_gui::termwindow::keyevent     > key_event RawKeyEvent { key: Physical(Space), modifiers: SHIFT, leds: (empty), phys_code: Some(Space), raw_code: 65, repeat_count: 1, key_is_down: true, handled: Handled(false) }
12:25:23.264  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(Space), modifiers: SHIFT, leds: (empty), phys_code: Some(Space), raw_code: 65, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }
12:25:23.264  INFO   wezterm_gui::termwindow::keyevent     > send to pane DOWN key=Char(' ') mods=SHIFT
12:25:23.264  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;2;32~", Char(' ') SHIFT
12:25:23.384  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: Char(' '), modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(Space), modifiers: SHIFT, leds: (empty), phys_code: Some(Space), raw_code: 65, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
12:25:23.384  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=Char(' ') mods=SHIFT
12:25:24.315  INFO   wezterm_gui::termwindow::keyevent     > key_event KeyEvent { key: LeftShift, modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: SHIFT, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }
12:25:24.315  INFO   wezterm_gui::termwindow::keyevent     > send to pane UP key=LeftShift mods=SHIFT
12:25:23.264  INFO   wezterm_term::terminalstate::keyboard > key_down: sending "\u{1b}[27;2;32~", Char(' ') SHIFT

Does not seem correct?

Stranger still, outside emacs, I get:

12:29:01.156  INFO   wezterm_term::terminalstate::keyboard > key_down: sending " ", Char(' ') SHIFT

😕

I have the same problem with shift-space and emacs. It looks like there was a similar problem with an older version of tmux (which tmux has since resolved):

https://github.com/wez/wezterm/issues/2774

So maybe this is a problem with emacs.

I am working around this by mapping shift-space to SendKey { key=' ' }.

markstos commented 5 months ago

I have perhaps the same problem, but I use Linux. What I have in common is that I use the QMK firmware. I have what they call "One Shot Mods", so if I want to send "Shift+Control+Space", I can normally tap Shift, tap, Control, then tap Space. QMK (supposedly) takes care sending to the host something that appears exactly the same as holding the modifiers before tapping the next key while they are held.

With Wezterm, I need to hold Shift and Control while pressing the next key. No other app I use responds this way to one-tap mods. For example, I tested the Foot terminal, and it has no problem with One Shot Mods.

To clarify, Wezterm has no issue with a single one-tap mods, only when two are chained together.

The below wezterm debug logs capture the difference. First, I use the hold method, and the in the second case, one shot mods. What I can see in the logs is that Wezterm is treating the Shift and Control as being released when using one-shot modes, or at least the Shift, so that it's registering "Control+X" instead of "Shift+Control+X".

I compared what happens when holding mods vs using one-shot mods while logging key events with wev as well. I'll include those full captures below, but there's the summary:

Using wev, while holding mods, wev sees:

  1. Press Control
  2. Press Shift
  3. Press X
  4. Release X
  5. Release Shift
  6. Release Control

Using wev, with QMK one-shot mods:

  1. Press Control
  2. Press Shift
  3. Press x
  4. Release X
  5. Release Control 👈🏼
  6. Release Shift 👈🏼

With one-shot mods, the modifiers are being released in the reverse order. I'm not sure if that's supposed to matter. It seems like either should be OK?


Wezterm with holding mods


21:12:30.276 DEBUG wezterm_gui::termwindow > RawKeyEvent(RawKeyEvent { key: Physical(LeftControl), modifiers: NONE, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) }) 21:12:30.276 INFO wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Physical(LeftControl), modifiers: NONE, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) } 21:12:30.276 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: LeftControl, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftControl), modifiers: NONE, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }) 21:12:30.276 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: LeftControl, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftControl), modifiers: NONE, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) }) } 21:12:30.276 INFO wezterm_gui::termwindow::keyevent > send to pane DOWN key=LeftControl mods=NONE 21:12:30.276 DEBUG wezterm_gui::termwindow > AdviseModifiersLedStatus(CTRL, (empty)) 21:12:30.476 DEBUG wezterm_gui::termwindow > RawKeyEvent(RawKeyEvent { key: Physical(LeftShift), modifiers: CTRL, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) 21:12:30.476 INFO wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Physical(LeftShift), modifiers: CTRL, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) } 21:12:30.476 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: LeftShift, modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: CTRL, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }) 21:12:30.476 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: LeftShift, modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: CTRL, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) } 21:12:30.476 INFO wezterm_gui::termwindow::keyevent > send to pane DOWN key=LeftShift mods=CTRL 21:12:30.476 DEBUG wezterm_gui::termwindow > AdviseModifiersLedStatus(SHIFT | CTRL, (empty)) 21:12:30.933 DEBUG wezterm_gui::termwindow > RawKeyEvent(RawKeyEvent { key: Physical(X), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) }) 21:12:30.933 INFO wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Physical(X), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) } 21:12:30.933 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: Char('X'), modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(X), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }) 21:12:30.933 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: Char('X'), modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(X), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) }) } 21:12:30.933 INFO wezterm_gui::termwindow::keyevent > Char('X') CTRL -> perform ActivateCopyMode 21:12:31.098 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: Char('X'), modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(X), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }) 21:12:31.098 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: Char('X'), modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(X), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }

===============================

After, with tapping mods

21:15:42.880 DEBUG wezterm_gui::termwindow > RawKeyEvent(RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) 21:15:42.880 INFO wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) } 21:15:42.880 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: LeftShift, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }) 21:15:42.881 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: LeftShift, modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: NONE, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: true, handled: Handled(false) }) } 21:15:42.881 INFO wezterm_gui::termwindow::keyevent > send to pane DOWN key=LeftShift mods=NONE 21:15:42.881 DEBUG wezterm_gui::termwindow > AdviseModifiersLedStatus(SHIFT, (empty)) 21:15:42.983 DEBUG wezterm_gui::termwindow > RawKeyEvent(RawKeyEvent { key: Physical(LeftControl), modifiers: SHIFT, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) }) 21:15:42.983 INFO wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Physical(LeftControl), modifiers: SHIFT, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) } 21:15:42.983 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: LeftControl, modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftControl), modifiers: SHIFT, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }) 21:15:42.983 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: LeftControl, modifiers: SHIFT, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(LeftControl), modifiers: SHIFT, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: true, handled: Handled(false) }) } 21:15:42.983 INFO wezterm_gui::termwindow::keyevent > send to pane DOWN key=LeftControl mods=SHIFT 21:15:42.983 DEBUG wezterm_gui::termwindow > AdviseModifiersLedStatus(SHIFT | CTRL, (empty)) 21:15:42.984 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: LeftShift, modifiers: SHIFT | CTRL, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }) 21:15:42.984 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: LeftShift, modifiers: SHIFT | CTRL, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftShift), modifiers: SHIFT | CTRL, leds: (empty), phys_code: Some(LeftShift), raw_code: 50, repeat_count: 1, key_is_down: false, handled: Handled(false) }) } 21:15:42.984 INFO wezterm_gui::termwindow::keyevent 👉🏼send to pane UP key=LeftShift mods=SHIFT | CTRL 21:15:42.984 DEBUG wezterm_gui::termwindow > AdviseModifiersLedStatus(CTRL, (empty)) 21:15:43.131 DEBUG wezterm_gui::termwindow 👉🏼 RawKeyEvent(RawKeyEvent { key: Physical(X), modifiers: CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) }) 21:15:43.131 INFO wezterm_gui::termwindow::keyevent > key_event RawKeyEvent { key: Physical(X), modifiers: CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) } 21:15:43.131 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: Char('x'), modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(X), modifiers: CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) }) }) 21:15:43.131 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: Char('x'), modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: true, raw: Some(RawKeyEvent { key: Physical(X), modifiers: CTRL, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: true, handled: Handled(false) }) } 21:15:43.131 INFO wezterm_gui::termwindow::keyevent > send to pane DOWN key=Char('x') mods=CTRL 21:15:43.131 INFO wezterm_term::terminalstate::keyboard > key_down: sending "\u{18}", Char('x') CTRL 21:15:43.252 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: LeftControl, modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftControl), modifiers: CTRL, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }) 21:15:43.252 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: LeftControl, modifiers: CTRL, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(LeftControl), modifiers: CTRL, leds: (empty), phys_code: Some(LeftControl), raw_code: 37, repeat_count: 1, key_is_down: false, handled: Handled(false) }) } 21:15:43.252 INFO wezterm_gui::termwindow::keyevent > send to pane UP key=LeftControl mods=CTRL 21:15:43.252 DEBUG wezterm_gui::termwindow > AdviseModifiersLedStatus(NONE, (empty)) 21:15:43.253 DEBUG wezterm_gui::termwindow > KeyEvent(KeyEvent { key: Char('x'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(X), modifiers: NONE, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: false, handled: Handled(false) }) }) 21:15:43.253 INFO wezterm_gui::termwindow::keyevent > key_event KeyEvent { key: Char('x'), modifiers: NONE, leds: (empty), repeat_count: 1, key_is_down: false, raw: Some(RawKeyEvent { key: Physical(X), modifiers: NONE, leds: (empty), phys_code: Some(X), raw_code: 53, repeat_count: 1, key_is_down: false, handled: Handled(false) }) } 21:15:43.253 INFO wezterm_gui::termwindow::keyevent > send to pane UP key=Char('x') mods=NONE

Using wev, while holding mods


[14: wl_keyboard] key: serial: 93354; time: 27783920; key: 37; state: 1 (pressed) sym: Control_L (65507), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000004: Control latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 93356; time: 27784101; key: 50; state: 1 (pressed) sym: Shift_L (65505), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000005: Shift Control latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 93358; time: 27784102; key: 53; state: 1 (pressed) sym: X (88), utf8: '' [14: wl_keyboard] key: serial: 93359; time: 27784280; key: 53; state: 0 (released) sym: X (88), utf8: '' [14: wl_keyboard] key: serial: 93360; time: 27784339; key: 50; state: 0 (released) sym: Shift_L (65505), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000004: Control latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 93362; time: 27784460; key: 37; state: 0 (released) sym: Control_L (65507), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000000 latched: 00000000 locked: 00000000

===============================

Using Wev, with QMK one-shot mods.

[14: wl_keyboard] key: serial: 92364; time: 27462333; key: 37; state: 1 (pressed) sym: Control_L (65507), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000004: Control latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 92366; time: 27462333; key: 50; state: 1 (pressed) sym: Shift_L (65505), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000005: Shift Control latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 92368; time: 27462333; key: 53; state: 1 (pressed) sym: X (88), utf8: '' [14: wl_keyboard] key: serial: 92369; time: 27462461; key: 37; state: 0 (released) sym: Control_L (65507), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000001: Shift latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 92371; time: 27462461; key: 50; state: 0 (released) sym: Shift_L (65505), utf8: '' [14: wl_keyboard] modifiers: serial: 0; group: 0 depressed: 00000000 latched: 00000000 locked: 00000000 [14: wl_keyboard] key: serial: 92373; time: 27462461; key: 53; state: 0 (released)

markstos commented 5 months ago

I found my issue with QMK one-shot mods is resolved by setting this in ~/.config/wezterm/wezterm.lua:

config.use_ime = false