How do you detect keystrokes in canonical mode?

I'm writing a command shell, and I want to be able to detect when the user presses an arrow key (otherwise it just prints [[A, [[B, etc.). I know it's relatively easy (although somewhat more time-consuming) to detect keystrokes in noncanonical mode, but I've noticed that the bash shell detects arrow keys in canonical mode. Originally I thought that there was some process signal that the shell receives when an escaped character is entered, but I've tested all the process signals in signal.h (defined in bits/signum.h) and none of them get sent when I press the arrow keys. How can you detect arrow keys in canonical mode?

You don't.

From man termios:

       In canonical mode:

       * Input  is  made  available  line by line.  An input line is available
         when one of the line delimiters is typed (NL, EOL, EOL2;  or  EOF  at
         the start of line).  Except in the case of EOF, the line delimiter is
         included in the buffer returned by read(2).

You'll get the [[A, etc. as arrow key escape-sequences after you hit ENTER. It's not stripping them out, but you won't get anything until you hit ENTER.

If you want individual keystrokes, use noncanonical (raw) mode.

Except I've noticed that the bash shell can detect the arrow keys (e.g. to move through history) without the user having to press enter, even though it shows ICANON turned on when I run stty. How does it do this?

It's not magic. Looking at bash 4.0's source code, specifically lib/sh/shtty.c:

int
tt_setonechar(ttp)
     TTYSTRUCT *ttp;
{
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)

  /* XXX - might not want this -- it disables erase and kill processing. */
  ttp->c_lflag &= ~ICANON;
...

The very first thing it does when setting the terminal to read input is turn off canonical mode.

It turns canonical mode back on when running external commands, because they expect and need it. stty is an external command, so...

1 Like

Okay, that makes sense now. Thanks for clearing that up.