How terminal emulators handle Shift+FKeys

rxvtterminalx11xterm

This question is inspired by another question on vi.SE. There, OP has issues with the Shift+F8 key combination when running vim inside urxvt. That key combination will never work in vim because the terminal emulator does not pass it correctly (at least from what I managed to debug).

I do not have a good understanding on how the F keys are handled by terminal emulators.

In urxvt

  • When I press Shift+F8 ~ is echoed.
  • When I press F7 (or any other F key) ~ is echoed as well.

In vim inside urxvt

  • When I press Shift+F8 I get the effect of ~~.
  • When I press Shift+F7 I get the effect of ~.
  • When I press Shift+F6 I get the effect of ~~~.

In xterm on the other hand:

  • When I press Shift+F8 ;2~ is echoed.
  • When I press F8 (or any other F key) only ~ is echoed.

I am at a loss in understanding why I get this output.

To debug further I did run xev and got the following:

For F8

KeyPress event, serial 29, synthetic NO, window 0x2000001,
    root 0x7e, subw 0x0, time 17730758, (431,256), root:(432,275),
    state 0x0, keycode 74 (keysym 0xffc5, F8), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

For left Shift

KeyPress event, serial 32, synthetic NO, window 0x2000001,
    root 0x7e, subw 0x0, time 17733031, (431,256), root:(432,275),
    state 0x0, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

And for right Shift

KeyPress event, serial 32, synthetic NO, window 0x2000001,
    root 0x7e, subw 0x0, time 17733372, (431,256), root:(432,275),
    state 0x0, keycode 62 (keysym 0xffe2, Shift_R), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

Yet, I see absolutely nothing strange in there.

How these keys (the F keys) are treated by terminal emulators? What do terminal emulators receive from x11? And how they pass it further to the program running inside them?

I always believed that the F keys were just a combination of Esc plus a digit. Now I found I was wrong.


Addendum

The F keys without Shift work well in vim inside urxvt. If I do:

:map <f8> :echo "yay"<CR>

I do correctly get the "yay" echoed out when I press F8.

Best Answer

The interface between the terminal and the application sends bytes, not keys. Printable characters are interpreted as the byte sequence corresponding to the character encoding of the terminal. Function keys are encoded as escape sequences. There are common conventions for those escape sequences but they aren't completely standardized.

For more general background, see How do keyboard input and text output work?. For more information, see also Is there any reason why I get ^[[A when I press up arrow at the console login screen? and key bindings table?

All function key escape sequences begin with the escape character and most of them end with ~. Vim recognizes a number of escape sequences based on its compile-time settings and the information it has on the terminal. If Vim doesn't recognize an escape sequence, it ignores it, but Vim doesn't know how long the escape sequence is (it doesn't assume that the last character is a ~, this isn't always the case). Often there's a stray ~ after the part that Vim recognizes, sometimes more.

You can see exactly what the terminal sends by pressing Ctrl+V first, either in a shell or in Vim's insert mode.

You can let Vim know about the function key corresponding to an escape sequence with :set, e.g.

:set <S-F8>=^[[19;2~

(replace the part after ^[ by what your terminal actually sends).

Related Question