How can I know that the user hit the ESC key in a console with ncurses (Linux)? - linux

I have a problem in detecting whether I just got a plain ESC key (just code 27) or whether it was another special key such as an Arrow Up which sends me three bytes: ESC [ A (27 91 65).
Now, I understand escape sequences, what I don't understand is how can I possibly know that the user actually typed ESC instead of a special key since both start with 27 and ESC is only 27?
Note that I use the wgetch() function from ncurses as in:
// initialization not shown initscr() should be enough for this test
while(!f_should_exit)
{
int c(wgetch(f_win_input));
// show codes of what the user types
//
printf("got [%d] ", c);
// prints 27 when I hit ESC
// prints 27 91 65 when I hit Arrow Up
}
I use the ESC and arrow keys all the time in vim so I would imagine that there is an easy way to specifically detect which key was pressed?!

This is a standard feature of X/Open Curses. The manual page for wgetch discusses it in keypad mode:
When a character that could be the beginning of a function key is received (which, on modern terminals, means an escape character), curses
sets a timer. If the remainder of the sequence does not come in within
the designated time, the character is passed through; otherwise, the
function key value is returned. For this reason, many terminals experience a delay between the time a user presses the escape key and the escape is returned to the program.
By default, keypad is not set to true for a given window, i.e., the library does not do this (your program must, if you want function-keys):
keypad(win, TRUE);
The timeouts are described in ncurses' input-options manual page. To distinguish an escape character from a function (or cursor, or keypad key), you could use notimeout, as mentioned in the discussion of nodelay:
While interpreting an input escape sequence, wgetch(3x) sets a timer
while waiting for the next character. If notimeout(win, TRUE) is
called, then wgetch does not set a timer. The purpose of the timeout
is to differentiate between sequences received from a function key and
those typed by a user.

Related

what is the vim native key comb that will show what is received on next input?

I forgot the key combination for this functionality and I tried to search a bit unfortunately to no avail.
I think it was actually a super simple combination of control or shift with a letter key. The next input in the editor will show the received key.
In command-line mode and insert mode, Ctrl-V is used to insert the next non-numeric keypress literally (i.e. not as a Vim movement or mapping, for example), or to enter a character by its numerical value. E.g.
Ctrl-VEnter will input a CR character (which Vim typically displays as ^M)
Ctrl-VTab will input a Tab character, even if the expandtabs option is set
Ctrl-VRight will input the text <Right>, since there is no one character associated with the right arrow key
Ctrl-V065 will input A, whose ASCII value is 65
Ctrl-VU1F4A9 will insert a poop emoji after you have typed a non-hex-digit
More info at :help i_CTRL-V, :help i_CTRL-V_digit, and equivalent command-line mode topic :help c_CTRL-V.

How screen handling program(eg.vi, less) work?

Suppose I'm reading a document in vi. When I reach the end of the screen and press the arrow down key, a new screen including the contents of the next line is printed in the terminal.
I wonder how this process is implemented.
Is vi process raise SIGTSTP and call signal handler to continue the process with new screen including next line?
Is ... process raise SIGTSTP and call signal handler ...
No.
The program first performs a tcsetattr() (which internally performs a special ioctl()) so the keyboard input is not handled line-wise but key-wise.
Then it prints out 25 lines.
Now it simply uses read() to wait for a key pressed.
If you press the DOWN key, one additional line is printed.
If you press the UP key (in the case of less), it is a bit more tricky; depending on the terminal used, some "escape sequence" (a special combination of bytes) is written to the screen using write(). This will cause the screen to be scrolled down. Then another "escape sequence" is written which places the cursor to the top left of the screen and one line of text is printed at the top of the screen.
When this is done, the program waits for the next key using read() ...
Signals are not involved.

Is there a way to programatically add escape sequences to ncurses

I am working on a ncursesw app that uses function keys.
Unfortunately it seems that there are some terminal emulators (notablly putty) that claim to be of terminal type "xterm" but send different escape sequences for the f1 to f4 keys from what a modern xterm sends (from some googling it seems that very old versions of xterm did the same).
ncursesw on my system just passes these escape sequences through to the app without interpreting them.
I would like to make my program accept these additional escape sequences for function keys. Is there a way to programatically add escape sequences to ncurses or will I need to write my own escape sequence interpreter?
Yes it is possible, using the call "define_key" ( http://invisible-island.net/ncurses/man/define_key.3x.html )
The documentation is not clear on whether it allows more than one escape sequence for a given "key" or not. My testing shows that it does allow it. So one can simply define the additional sequences.
define_key("\e[11~",KEY_F1);
define_key("\e[12~",KEY_F2);
define_key("\e[13~",KEY_F3);
define_key("\e[14~",KEY_F4);
You may want to surround this with a termname check so it only applies when the claimed terminal type is xterm (I did in my actual program but my actual program was written in pascal).

Is there a typeahead buffer for key presses in Linux terminal?

Does Linux buffer keys typed in terminal so that you can read them later one key at a time?
I am asking because I want to catch ESC and arrow key presses and can't find a way to reliably read the codes. I put terminal in non-canonical mode and want program to block if there is no input, but if there is, I want to fetch only one key press for processing.
Update 2: Arrow keys is just an example. I need to identify key presses even for the keys with unknown escape sequences for my program.
There are two conflicting cases:
read(1) returns one character. For both function keys and ESC key this character will be 0x1b. To check if it was an arrow key, you need to read(1), which will block if only single ESC is pressed.
Solution: blocked read(1), non-blocked read(1)
Problem: if second read didn't match any function key, it may mean that it was buffered ESC followed by some sequence, or an unknown function key. How to detect unknown function key press?
read(4) returns at most 4 characters, but if you press ESC four times to let it buffer, you'll get a string of four 0x1b. The same problem to find out if there is an unknown function key press as above.
Can anybody explain how to deal with these problems in Linux terminal, or at least post a proof that Linux just doesn't have input buffer for keys?
You should read up on VT100 escape sequences.
You've discovered that the character code for the escape button (which is sent as a real character, but tends to be used almost exclusively for signalling the beginning of an escape sequence) is 0x1b.
To move the cursor UP: <ESC>[{COUNT}A
To move the cursor DOWN: <ESC>[{COUNT}B
To move the cursor RIGHT: <ESC>[{COUNT}C
To move the cursor LEFT: <ESC>[{COUNT}D
You can test these yourself by typing them into the terminal. Just type the keys one after another. My terminal does not recognize the count argument, but will work successfully if I type <ESC>[X (for X in A,B,C,D).
If your terminal isn't in VT100 mode, look up the escape sequences for whichever mode it's in. You might realize that depending too much on terminal specific escape codes restricts your program to one specific terminal type.

Mapping numeric keypad keys in vim

I'm unable to get this mapping to work in vim inside an xterm terminal.
:map <k0> :echo 'Hello'<CR>
I can get the same mapping to work fine in gvim. If I issue the above command in vim on a terminal, it accepts it, and it shows up correctly when I type :map. But in normal mode, if I press the 0 keypad key, a "0" shows up on the status line, and then disappears with the next keypress.
I'm using the vim that came with Fedora 14 if that matters, and a plain xterm. The keypad keys work fine in insert mode, both with numlock on and off.
What am I missing?
Try to add this line to your ~/.Xdefaults:
xterm*appkeypadDefault: false
and relaunch xterm.
The "Application Keypad Mode" is likely the reason of your troubles.
But I don't think you should do what you are doing. In --NORMAL-- mode, numeric input is used to indicate "count" like in 4dd. Mapping numbers to other commands is going to get you into troubles fast.
You should add a xterm tag to your question.
There's some ambiguity in the question, which may indicate the actual problem. vim accepts that binding for k0 supposing that it is a function key.
Most keyboards that you'll see number function-keys starting at 1, and a few terminal descriptions equate function-key 1 to k0, a few equate it k0 to function-key 10. It's also possible that someone assumes that is part of the numeric keypad, but unlikely (since the keypad uses different character sequences than the function keys).
That's assuming you used a terminal description that knows about the function keys. The vt100 terminal description doesn't do that, since vt100's had no function keys (other than PF1 through PF4 which are or aren't depending who you talk to). But if you had TERM=vt100, then some of the numeric keypad could be recognized on the basis of the terminal description (see for instance the lengthy comment above the vt100+fnkeys description).
It's not in TERM=xterm, however.
What you're overlooking is that vim (helpfully perhaps) amends the terminal description using its built-in termcaps. It recognizes PF1, etc. using table entries like this:
{K_XF1, IF_EB("\033O*P", ESC_STR "O*P")},
{K_XF2, IF_EB("\033O*Q", ESC_STR "O*Q")},
{K_XF3, IF_EB("\033O*R", ESC_STR "O*R")},
{K_XF4, IF_EB("\033O*S", ESC_STR "O*S")},
But there are no entries for the numbered keys; there's no "\033O*p" for the 0 key.
If vim has (in the terminal description) the k0, and you haven't mapped it to anything, vim will treat it as a literal 0. The same happens with k1, etc., in effect treating the function-keys and numeric keypad as the same thing.
For what it's worth, GNU screen does the same thing, but also for the numbered keys. If I run vim inside screen, vim will see only the 0's. A literal 0 in vim doesn't do much in command-mode.

Resources