Different control codes on arrow keys when using zsh #19796

Closed
opened 2026-01-31 06:53:52 +00:00 by claunia · 6 comments
Owner

Originally created by @eggbean on GitHub (May 2, 2023).

The control codes for the arrow keys on Windows Terminal are the same as on Linux terminals when using bash, but when using zsh they are different.

Why is this?

At the moment I am working around this using this conditional:

# Key bindings
bindkey '^[[A'  history-beginning-search-backward
bindkey '^[[B'  history-beginning-search-forward
bindkey '^[[C'  forward-char
bindkey '^[[D'  backward-char

# Workarounds for Windows Terminal
if [[ -n $WT_SESSION ]]; then
  bindkey '^[OA' history-beginning-search-backward
  bindkey '^[OB' history-beginning-search-forward
  bindkey '^[OC' forward-char
  bindkey '^[OD' backward-char
  export COLORTERM='truecolor'
fi

It's not necessary to set the bindings for left/right, but I am including them here as the codes are different.

Are there any other codes that are different? Is there a better way of doing this? I would prefer Windows Terminal to use the came codes in zsh as it does in bash and on Linux.

Originally created by @eggbean on GitHub (May 2, 2023). The control codes for the arrow keys on Windows Terminal are the same as on Linux terminals when using bash, but when using zsh they are different. Why is this? At the moment I am working around this using this conditional: ``` # Key bindings bindkey '^[[A' history-beginning-search-backward bindkey '^[[B' history-beginning-search-forward bindkey '^[[C' forward-char bindkey '^[[D' backward-char # Workarounds for Windows Terminal if [[ -n $WT_SESSION ]]; then bindkey '^[OA' history-beginning-search-backward bindkey '^[OB' history-beginning-search-forward bindkey '^[OC' forward-char bindkey '^[OD' backward-char export COLORTERM='truecolor' fi ``` It's not necessary to set the bindings for left/right, but I am including them here as the codes are different. Are there any other codes that are different? Is there a better way of doing this? I would prefer Windows Terminal to use the came codes in zsh as it does in bash and on Linux.
claunia added the Needs-TriageNeeds-Tag-Fix labels 2026-01-31 06:53:52 +00:00
Author
Owner

@j4james commented on GitHub (May 2, 2023):

I'm assuming this is because zsh (or something you're using) is requesting the cursor key application mode (DECCKM). There's a long discussion about this in the zsh FAQ, but it wasn't clear to me from reading that whether they enable the mode by default or not. But if your .zshrc file has smkx in it somewhere, then you're probably turning it on.

@j4james commented on GitHub (May 2, 2023): I'm assuming this is because zsh (or something you're using) is requesting the cursor key application mode ([`DECCKM`](https://www.vt100.net/docs/vt510-rm/DECCKM)). There's a long discussion about this in the [zsh FAQ](https://zsh.sourceforge.io/FAQ/zshfaq03.html#l26), but it wasn't clear to me from reading that whether they enable the mode by default or not. But if your `.zshrc` file has `smkx` in it somewhere, then you're probably turning it on.
Author
Owner

@eggbean commented on GitHub (May 2, 2023):

Thanks. So this isn't something that happens for every Windows Terminal and zsh user? There's no mention of smkx in my configuration.

It looks like that using the terminfo codes in the FAQ you linked to would be better than using the raw control codes I used above, but now I have found another way to get the partial history completion while also not losing the ability to move up on multiline commands and while also moving the cursor to the end of the input (like in bash), so I am just going to do this now.

for direction (up down) {
  autoload $direction-line-or-beginning-search
  zle -N $direction-line-or-beginning-search
  key=$terminfo[kcu$direction[1]1]
  for key ($key ${key/O/[})
    bindkey $key $direction-line-or-beginning-search
}

From here. Cheers.

@eggbean commented on GitHub (May 2, 2023): Thanks. So this isn't something that happens for every Windows Terminal and zsh user? There's no mention of `smkx ` in my configuration. It looks like that using the terminfo codes in the FAQ you linked to would be better than using the raw control codes I used above, but now I have found another way to get the partial history completion while also not losing the ability to move up on multiline commands and while also moving the cursor to the end of the input (like in bash), so I am just going to do this now. ``` for direction (up down) { autoload $direction-line-or-beginning-search zle -N $direction-line-or-beginning-search key=$terminfo[kcu$direction[1]1] for key ($key ${key/O/[}) bindkey $key $direction-line-or-beginning-search } ``` [From here.](https://unix.stackexchange.com/a/691482/1932) Cheers.
Author
Owner

@j4james commented on GitHub (May 2, 2023):

So this isn't something that happens for every Windows Terminal and zsh user?

I've just installed zsh, and skipped the configuration step, so it's assumedly using the default config. If I then record a session with script, I can see that it's enabling DECCKM after every prompt, and then disabling it again before executing whatever command I enter.

So this appears to be something that's specifically requested by the zsh line editor. I would expect it to be the same for all zsh users, on all terminals, at least by default. It's possible zsh alters that behavior dependent on the TERM type or something like that, but it's definitely not specific to Windows Terminal.

@j4james commented on GitHub (May 2, 2023): > So this isn't something that happens for every Windows Terminal and zsh user? I've just installed zsh, and skipped the configuration step, so it's assumedly using the default config. If I then record a session with `script`, I can see that it's enabling `DECCKM` after every prompt, and then disabling it again before executing whatever command I enter. So this appears to be something that's specifically requested by the zsh line editor. I would expect it to be the same for all zsh users, on all terminals, at least by default. It's possible zsh alters that behavior dependent on the `TERM` type or something like that, but it's definitely not specific to Windows Terminal.
Author
Owner

@eggbean commented on GitHub (May 2, 2023):

It wasn't happening on remote servers through ssh (with identical dotfiles) that had zsh as the default shell (hence the configuration I was using above), only on my local laptop. That's why I assumed it was a Windows Terminal issue.

But with the second configuration none of this is needed anymore. Cheers.

@eggbean commented on GitHub (May 2, 2023): It wasn't happening on remote servers through ssh (with identical dotfiles) that had zsh as the default shell (hence the configuration I was using above), only on my local laptop. That's why I assumed it was a Windows Terminal issue. But with the second configuration none of this is needed anymore. Cheers.
Author
Owner

@binfeiruci commented on GitHub (Dec 3, 2025):

It is oh-my-zsh.

e9fc134236/lib/key-bindings.zsh (L9)

if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
  function zle-line-init() {
    echoti smkx
  }
  function zle-line-finish() {
    echoti rmkx
  }
  zle -N zle-line-init
  zle -N zle-line-finish
fi

I'm assuming this is because zsh (or something you're using) is requesting the cursor key application mode (DECCKM). There's a long discussion about this in the zsh FAQ, but it wasn't clear to me from reading that whether they enable the mode by default or not. But if your .zshrc file has smkx in it somewhere, then you're probably turning it on.

@binfeiruci commented on GitHub (Dec 3, 2025): It is oh-my-zsh. https://github.com/ohmyzsh/ohmyzsh/blob/e9fc134236323ce3ce376715b1e55a54ed6ac7ac/lib/key-bindings.zsh#L9 ```zsh if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then function zle-line-init() { echoti smkx } function zle-line-finish() { echoti rmkx } zle -N zle-line-init zle -N zle-line-finish fi ``` > I'm assuming this is because zsh (or something you're using) is requesting the cursor key application mode ([`DECCKM`](https://www.vt100.net/docs/vt510-rm/DECCKM)). There's a long discussion about this in the [zsh FAQ](https://zsh.sourceforge.io/FAQ/zshfaq03.html#l26), but it wasn't clear to me from reading that whether they enable the mode by default or not. But if your `.zshrc` file has `smkx` in it somewhere, then you're probably turning it on.
Author
Owner

@eggbean commented on GitHub (Dec 3, 2025):

I don't use oh-my-zsh. It's terrible!

@eggbean commented on GitHub (Dec 3, 2025): I don't use oh-my-zsh. It's terrible!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#19796