Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to Arrow Keys in Readline Library

Tags:

c

readline

I'm trying to bind to arrow keys in C using the GNU Readline library. I've printed out all possible combinations of ASCII (0-255) key values of rl_bind_key, but the arrow keys don't seem to be detected.

How can I bind to them?

static int readline_func(int count, int key) {
   printf("key pressed\n");
   return 0;
}

int i = 0;
for (; i < 255; i++) {
    rl_bind_key(i, readline_func);
}

Thanks for your help!

like image 373
James Taylor Avatar asked Oct 11 '16 02:10

James Taylor


Video Answer


1 Answers

It's not quite that simple to bind arrow keys.

First, arrow keys are not single characters; they are character sequences. The precise character sequence sent by each arrow key will depend on your terminal emulator, but if you're using Linux with an xterm-like terminal emulator, you'll probably find that the arrow keys send the sequences:

  • ↑  ESC[A
  • ↓  ESC[B
  • ESC[C
  • ESC[D

(You can find more information in the useful compendium of control sequences maintained by Thomas Dickey. Note that CSI is the "Control Sequence Initiator" ESC[).

To bind these sequences, you'll need to use rl_bind_keyseq, specifying a readline-style "keyseq" (see man readline; search for keyseq). For example, to bind right-arrow to a function, you could call:

rl_bind_keyseq("\\e[C", right_arrow_function);

The initial readline bindings include bindings for all standard keys, using a variety of different possible key-to-keyseq mappings in case the terminal emulator is using some other emulation.

You might think that using rl_bind_key to bind the escape key (0x1B) would override that key sequence (since it starts with an escape), but that's not how readline works.

Readline binds multi-character sequences using a trie of linked keymaps, where each key in the sequence is bound to the next keymap in turn. The precise mechanism isn't important -- readline will deal with it -- but you need to know that it is not possible to bind a prefix of a bound key sequence, because the prefix will be bound to a sub-keymap and the sub-keymap binding takes precedence over any other kind of binding.

So any attempt to rebind a prefix of a bound keyseq will fail without any error indication; rl_bind_key (or rl_bind_keyseq) will return 0 even though the binding has no effect. Consequently, your loop over all the possible 8-bit characters would have attempted to bind ESC, and the call to rl_bind_key would have succeeded, but the escape key would remain bound to the sub-keymap. (I personally find the lack of an error return irritating; it would be nice to get an error indication.)

When the rl_command_function is called, by the way, the key argument will contain the code for the last key in the sequence. (That's a result of the fact that it is the last key which actually holds the binding information, in the corresponding sub-keymap.) That's OK for arrow keys, but it is annoying for binding the large number of keys, including PageUp and PageDown, whose code sequences have the form ESC[p~ where p is some (possibly multidigit) number which identifies the key, but the last character in all the sequences is ~.

like image 145
rici Avatar answered Oct 20 '22 18:10

rici