39

I'm running zsh as the default shell on a Ubuntu box, and everything works fine using gnome-terminal (which as far as I know emulates xterm). When I login from a windows box via ssh and putty (which also emulates xterm) suddendly the home/end keys no longer work.

I've been able to solve that adding these lines to my zshrc file...

bindkey '\e[1~' beginning-of-line
bindkey '\e[4~' end-of-line

...but I'm still wondering what's wrong here. Any idea?

Myrddin Emrys
  • 36,888
  • 10
  • 36
  • 47
agnul
  • 11,222
  • 14
  • 60
  • 83

7 Answers7

77

I found it's a combination:

One

The ZSH developers do not think that ZSH should define the actions of the Home, End, Del, ... keys.

Debian and Ubuntu fix this by defining the normal actions the average user would expect in the global /etc/zsh/zshrc file. Following the relevant code (it is the same on Debian and Ubuntu):

if [[ "$TERM" != emacs ]]; then
[[ -z "$terminfo[kdch1]" ]] || bindkey -M emacs "$terminfo[kdch1]" delete-char
[[ -z "$terminfo[khome]" ]] || bindkey -M emacs "$terminfo[khome]" beginning-of-line
[[ -z "$terminfo[kend]" ]] || bindkey -M emacs "$terminfo[kend]" end-of-line
[[ -z "$terminfo[kich1]" ]] || bindkey -M emacs "$terminfo[kich1]" overwrite-mode
[[ -z "$terminfo[kdch1]" ]] || bindkey -M vicmd "$terminfo[kdch1]" vi-delete-char
[[ -z "$terminfo[khome]" ]] || bindkey -M vicmd "$terminfo[khome]" vi-beginning-of-line
[[ -z "$terminfo[kend]" ]] || bindkey -M vicmd "$terminfo[kend]" vi-end-of-line
[[ -z "$terminfo[kich1]" ]] || bindkey -M vicmd "$terminfo[kich1]" overwrite-mode

[[ -z "$terminfo[cuu1]" ]] || bindkey -M viins "$terminfo[cuu1]" vi-up-line-or-history
[[ -z "$terminfo[cuf1]" ]] || bindkey -M viins "$terminfo[cuf1]" vi-forward-char
[[ -z "$terminfo[kcuu1]" ]] || bindkey -M viins "$terminfo[kcuu1]" vi-up-line-or-history
[[ -z "$terminfo[kcud1]" ]] || bindkey -M viins "$terminfo[kcud1]" vi-down-line-or-history
[[ -z "$terminfo[kcuf1]" ]] || bindkey -M viins "$terminfo[kcuf1]" vi-forward-char
[[ -z "$terminfo[kcub1]" ]] || bindkey -M viins "$terminfo[kcub1]" vi-backward-char

# ncurses fogyatekos
[[ "$terminfo[kcuu1]" == "^[O"* ]] && bindkey -M viins "${terminfo[kcuu1]/O/[}" vi-up-line-or-history
[[ "$terminfo[kcud1]" == "^[O"* ]] && bindkey -M viins "${terminfo[kcud1]/O/[}" vi-down-line-or-history
[[ "$terminfo[kcuf1]" == "^[O"* ]] && bindkey -M viins "${terminfo[kcuf1]/O/[}" vi-forward-char
[[ "$terminfo[kcub1]" == "^[O"* ]] && bindkey -M viins "${terminfo[kcub1]/O/[}" vi-backward-char
[[ "$terminfo[khome]" == "^[O"* ]] && bindkey -M viins "${terminfo[khome]/O/[}" beginning-of-line
[[ "$terminfo[kend]" == "^[O"* ]] && bindkey -M viins "${terminfo[kend]/O/[}" end-of-line
[[ "$terminfo[khome]" == "^[O"* ]] && bindkey -M emacs "${terminfo[khome]/O/[}" beginning-of-line
[[ "$terminfo[kend]" == "^[O"* ]] && bindkey -M emacs "${terminfo[kend]/O/[}" end-of-line
fi

So, if you are connecting to a Debian or Ubuntu box, you don't have to do anything. Everything should work automagically (if not, see below).

But... if you are connecting to another box (e.g. FreeBSD), there might be no user friendly default zshrc. The solution is of course to add the lines from the Debian/Ubuntu zshrc to your own .zshrc.

Two

Putty sends xterm as terminal type to the remote host. But messes up somewhere and doesn't send the correct control codes for Home, End, ... that one would expect from an xterm. Or an xterm terminal isn't expected to send those or whatever... (Del key does work in xterm however, if you configure it in ZSH). Also notice that your Numpad-keys act funny in Vim for example with xterm terminal.

The solution is to configure Putty to send another terminal type. I've tried xterm-color and linux. xterm-color fixed the Home/End problem, but the Numpad was still funny. Setting it to linux fixed both problems.

You can set terminal type in Putty under Connection -> Data. Do not be tempted to set your terminal type in your .zshrc with export TERM=linux, that is just wrong. The terminal type should be specified by your terminal app. So that if, for example, you connect from a Mac box with a Mac SSH client it can set it's own terminal type.

Notice that TERM specifies your terminal type and has nothing to do with the host you are connecting to. I can set my terminal type to linux in Putty and connect to FreeBSD servers without problems.

So, fix both these things and you should be fine :)

Pit
  • 2,283
  • 21
  • 33
hopla
  • 3,072
  • 4
  • 26
  • 26
  • 3
    Another waay to fix the funny keypad behaviour: Open the session settins, go to `Terminal -> Features` and check the box labelled `Disable application keypad mode`. See here: http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter4.html#config-appkeypad – exhuma Apr 05 '10 at 09:36
  • 1
    I had to add ``vi-beginning-of-line`` and ``vi-end-of-line`` bindings to viins mode aswell to completely fix my problem with zsh + putty. Now it works though. – Daniel Baulig Dec 01 '11 at 20:16
  • I didn't have success with the .zshrc code that @hopla pasted. So I had to use the solution on the following zsh wiki page [http://zshwiki.org/home/zle/bindkeys](http://zshwiki.org/home/zle/bindkeys) in the section about reading from $terminfo[] plus hopla's solution about setting the terminal type to "linux". The ZSH wiki page says to do basically the same but without the "-M ..." options passed to bindkey. This only works in the "vi" bindkey mode for me though. – Alex Q Feb 15 '12 at 02:31
  • @exhuma fix worked for me. I was having trouble driving emacs on a CentOS5 box from Windows via PuTTy -> zsh -> tmux -> emacs – BungleFeet Feb 04 '14 at 10:42
  • The terminal type for PuTTY has been `putty` in Dickey terminfo since 2001. https://invisible-island.net/ncurses/terminfo.ti.html#tic-putty – JdeBP Jan 06 '20 at 14:37
18

On the PuTTY configuration dialog, go to Connection -> Data and type linux into the Terminal-type string before connecting.

Adam Pierce
  • 30,917
  • 21
  • 66
  • 86
6

This is working for me

bindkey -v

bindkey '\eOH'  beginning-of-line
bindkey '\eOF'  end-of-line
Rene
  • 433
  • 1
  • 7
  • 10
5

the appropriate answer that should be portable across all distros (not necessarly all versions of zsh though, ymmv here) is to use the zkbd helper utility from zkbd.

Keyboard Definition
The large number of possible combinations of keyboards, workstations, terminals, emulators, and window systems makes it impossible for zsh to have built-in key bindings for every situation. The zkbd utility, found in Functions/Misc, can help you quickly create key bindings for your configuration.

Run zkbd either as an autoloaded function, or as a shell script:

zsh -f ~/zsh-4.3.17/Functions/Misc/zkbd

When you run zkbd, it first asks you to enter your terminal type; if the default it offers is correct, just press return. It then asks you to press a number of different keys to determine characteristics of your keyboard and terminal; zkbd warns you if it finds anything out of the ordinary, such as a Delete key that sends neither ^H nor ^?.

The keystrokes read by zkbd are recorded as a definition for an associative array named key, written to a file in the subdirectory .zkbd within either your HOME or ZDOTDIR directory. The name of the file is composed from the TERM, VENDOR and OSTYPE parameters, joined by hyphens.

You may read this file into your .zshrc or another startup file with the source' or.' commands, then reference the key parameter in bindkey commands, like this:

          source ${ZDOTDIR:-$HOME}/.zkbd/$TERM-$VENDOR-$OSTYPE
          [[ -n ${key[Left]} ]] && bindkey "${key[Left]}" backward-char
          [[ -n ${key[Right]} ]] && bindkey "${key[Right]}" forward-char
          # etc.

Note that in order for `autoload zkbd' to work, the zkdb file must be in one of the directories named in your fpath array (see zshparam(1)). This should already be the case if you have a standard zsh installation; if it is not, copy Functions/Misc/zkbd to an appropriate directory.

see man -P "less -p 'keyboard definition'" zshcontrib, or search the meta-manpage zshall

simont
  • 57,012
  • 16
  • 110
  • 130
Josh McGee
  • 292
  • 2
  • 5
4

It's now been nearly 11 years since this question was first posted. At the time, some distros did ship with a putty terminfo entry, but it was mediocre at best. In the years since, the situation has improved, and the hacks that were necessary for over a decade are no longer required. PuTTY still defaults to setting TERM to xterm for compatibility, but if you're connecting to modern, up-to-date systems, you'll likely have luck overriding this and setting it to putty-256color:

  1. Ensure the host has a terminfo entry for putty-256color: toe -a | grep -F putty
  2. Undo any hacks you may have enabled to get PuTTY working properly with zsh or other programs.
  3. Ensure PuTTY is up-to-date. It won't notify you when updates are available, and if it's out-of-date, you're likely going to run into a lot of the same issues. You may want to keep it up-to-date automatically with something like Chocolatey.
  4. In PuTTY's configuration dialog, go to Connection -> Data and set "Terminal-type string" to putty-256color.
  5. While you're at it, on the same configuration screen, add a new environment variable to enable 24-bit color. This variable isn't standardized, but it's sent by a number of other mainstream terminal emulators (e.g., iTerm2), and many programs understand it.
    1. Variable: COLORTERM
    2. Value: truecolor
  6. As of writing, I haven't found a distro that accepts the COLORTERM variable over SSH by default. You'll need to edit your OpenSSH configuration on the host to allow it. For example, on Debian-like distros, edit /etc/ssh/sshd_config and add COLORTERM to the AcceptEnv line.
  7. Everything should now "just work". If it doesn't:
    1. Ensure you've reconnected after making the change, or at least run exec zsh after changing TERM. zsh won't react to changes in TERM while it's running.
    2. Ensure that TERM is actually set to what you intended: echo $TERM
    3. Are you on the latest version of your distro? If you're on a long-term support lifecycle build, for example, even if your version is technically still supported, it may not have up-to-date terminfo entries.
    4. Are you using screen or tmux? That's another whole can of worms. Test without those first to narrow down where the issue is occurring. Within tmux, try setting TERM=tmux-256color. Within screen, try TERM=screen-256color.
    5. Are you on the latest version of PuTTY?
    6. Do you have RC-files that are implementing keybindings or other hacks? Try using default RC-files.
    7. Did you already change various PuTTY settings to attempt to fix the issue before attempting the terminfo fix? You'll probably need to reset those settings.
Zenexer
  • 16,313
  • 6
  • 62
  • 72
  • This worked for me on Gentoo - and Gentoo accepted COLORTERM without changes. Gentoo also has a screen terminfo for putty-256color so I'd expect screen to work as well. – ZiggyTheHamster May 22 '20 at 18:47
  • I do not have Connection --> Data in PuTTY 0.73 – Eric Jun 04 '20 at 19:54
3

It seems a putty thing. Gnome-terminal sends the codes ^[OH and ^[OF for Home and End respectively, while putty sends ^[[1~ and ^[[4~. There's an option in putty to change the Home/End keys from standard mode to rxvt mode, and that seems to fix the Home key, but not the End key (which now sends ^[Ow). Guess it's time to file a bug report somewhere... :-)

agnul
  • 11,222
  • 14
  • 60
  • 83
0

These bindings simply don't appear to be part of the default bindings set in emacs mode.

executing "where-is beginning-of-line" on my default zsh installation after running "bindkey -e" shows it is only bound to ^a. Perhaps you should ask the zsh developers why :-)

Sec
  • 6,546
  • 5
  • 29
  • 57
  • The default ubuntu install binds them in a global zshrc file, but as far as I can see the bindings don't work for putty. – agnul Oct 02 '08 at 13:01