5

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!

James Taylor
  • 6,158
  • 8
  • 48
  • 74

2 Answers2

9

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

rici
  • 234,347
  • 28
  • 237
  • 341
  • 1
    This is an excellent answer! I surprised there isn't more documentation on this. Thanks so much! – James Taylor Oct 11 '16 at 05:40
  • 1
    @rici That was really a nice answer! I had been searching for this for almost entire day... I will try this – hrushi Feb 22 '17 at 03:42
0

ASCII encoding character set, as you can see, has no concept of special keys like arrow key, that your run-of-the-mill keyboard provides.

Its your platforms keyboard driver send sequence of byte(s) to your program, so answer would be platform dependent. On Linux, e.g, termcap provides this information and you can access this information via ncurses library from your C prgram.

Check this article.

JamesWebbTelescopeAlien
  • 3,547
  • 2
  • 30
  • 51