4

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.

grochmal
  • 8,657

2 Answers2

3

A few keys are used as modifiers (shift, control are the most used). Terminal emulators receive a series of X events, which you can see with xev. The terminal emulator combines some of those events such as shifta using X libraries to get A. For other cases such as function-keys and cursor-keys (called "special keys") there is no predefined transformation by the X libraries. The terminal emulator decides if and when to use these modifiers to make different escape sequences sent by special keys.

Different terminal emulators can use different rules for this: there is no standard for the sequences which can be sent. There are only conventions, e.g., deciding to imitate certain terminals, or extend things by making new sequences which no other terminal does.

Both rxvt and xterm use these modifiers to provide different function-key sequences, allowing programs running in the terminals to act as if your terminal had a few dozen function-keys.

However - the terminals can also have a mode, set by control sequences from the application to the terminal that change the escape sequences sent for function-keys. It is common to use these mode-switching control sequences in full-screen programs (such as vim). So you see differences.

Because there is no standard, there is a terminal description for each terminal, which describes its behavior (using the TERM environment variable). For most terminals, the description initializes the terminal to use application mode (for both the keypad and cursor-keys). Full-screen applications use these initialization sequences and use the function- and cursor-keys listed in those descriptions, so that they get consistent behavior.

Not all terminals have sent escape-sequences for function-keys. For example, the altos workstation used a caret character, wyse-85 supported an 8-bit mode using an \233 rather than \033[ (CSI), qnx used \377. But most began with the ASCII escape character. Final characters are a different story. About half of rxvt's modified function-keys end with a character other than ASCII tilde ~ (there's a table in the ncurses FAQ illustrating this). Because of these differences, it is important to have the terminal description match the actual terminal.

In the current version of vim (7.3), I see no problem. It recognizes shifted F8. A few years ago, there was a problem. vim's check for function-keys did not expect a semicolon, as is sent by xterm. As soon as it encounted the semicolon, it stopped recognizing the function-key. You may have an old version of vim, or some key-mappings which interfere with it recognizing the keys. vim may now (in whatever version you are using), but did not look at the terminal description for the extended keys, but relied on its own tables. To complicate the situation, vim may use an xterm feature (called tcap-query) to obtain the actual key sequences sent by xterm.

On the other hand, vim probably has a mapping which gives different results for the rxvt key sequences. There is no special distinction between its shifted F6 (\E[29~), shifted F7 (\E[31~) and shifted F8 (\E[32~).

Further reading:

Thomas Dickey
  • 76,765
  • ECMA48 (8.3.52) provides standard codes for function keys: [CSI] n [SPACE] G. no privision is made for shift,ctrl,alt, etc. + func. – Jasen Jun 28 '16 at 01:11
  • 2
    The other half of your comment would point out the extensive list of terminals implementing this feature. – Thomas Dickey Jun 28 '16 at 01:15
  • hmm, yeah... strange that there's basically none. – Jasen Jun 28 '16 at 01:18
  • I've yet to find a terminal emulator that generates FNK control sequences. DECFNK is common, but as M. Dickey says, there's a wide degree of variability. – JdeBP Jun 28 '16 at 08:03
  • I have some reading to do, somehow i always feared the termcap/terminfo files, thanks for the extra reading. Just one thing I want to add is that contrary to OP in the linked question I managed to replicate this behaviour without tmux. I can replicate this by simply setting TERM=rxvt-unicode-256color – grochmal Jun 28 '16 at 13:36
3

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).

  • Re: fixing escape sequences in Vim, it's more practical to do it the other way around, map <Esc>[19;2~ <S-F8>. This way you can have multiple such sequences translate to <S-F8>, to cope with different TERMs. The real solution however is to fix the corresponding entries in termcap / terminfo. – Satō Katsura Jun 28 '16 at 04:36
  • You probably should answer the question on vi.SE as well. This clarified a lot to me: X11 receives bytes, transforms them into events, sends the events to the terminal emulator, which in turn transforms them back into bytes (correct?). I guess, i finally understood why it is called an emulator. I have some reading to do and likely will come with a follow up question (as another question). Thanks. – grochmal Jun 28 '16 at 13:40