In a program that uses the curses
or ncurses
library, the getch()
function can recognize either an ordinary character (like 'x'
) or the escape sequence sent by the arrow and function keys. For example, typing the up arrow key might send the sequence (Escape, '[', 'A'), but getch()
will return the int
value KEY_UP
. It even uses timing information to distinguish between an Escape character sent by itself and an Escape character that's part of a sequence.
To use getch()
successfully, you first have to call initscr()
, which clears the screen so that curses can control what's displayed.
Is there a convenient way to recognize special keys without taking over the screen? Ideally I'd like to call getch()
without first calling initscr()
, and have it return the same value it would have returned if I had called initscr()
, but experiment indicates that doesn't work.
Here's a non-working example of what I'm trying to do. The program prompts the user to enter Up, Down, Escape, Left, Right, and conforms that the correct keys were entered.
Running the program with -c
invokes initscr()
and allows it to work correctly. Running it without -c
calls getch()
without first calling initscr()
; all the getch()
calls fail without waiting for input, returning ERR
(-1
).
// gcc arrows.c -o arrows -lncurses
#include <term.h>
#include <stdio.h>
#include <curses.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int use_curses = 0;
if (argc == 2 && strcmp(argv[1], "-c") == 0) {
use_curses = 1;
}
else if (argc != 1) {
fprintf(stderr, "Usage: %s [-c]\n", argv[0]);
exit(EXIT_FAILURE);
}
WINDOW *win;
if (use_curses) {
win = initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
mvaddstr(0, 0, "Press UP, DOWN, Escape, LEFT, RIGHT");
}
else {
puts("Press UP, DOWN, Escape, LEFT, RIGHT");
}
int keys[5];
for (int i = 0; i < 5; i ++) {
keys[i] = getch();
}
int ok;
if (keys[0] == KEY_UP &&
keys[1] == KEY_DOWN &&
keys[2] == '\x1b' &&
keys[3] == KEY_LEFT &&
keys[4] == KEY_RIGHT)
{
ok = 1;
if (use_curses) {
mvaddstr(2, 0, "OK");
}
else {
puts("OK");
}
}
else {
ok = 0;
char message[100];
sprintf(message,
"Incorrect input: (%d, %d, %d, %d, %d)",
keys[0], keys[1], keys[2], keys[3], keys[4]);
if (use_curses) {
mvaddstr(2, 0, message);
}
else {
puts(message);
}
}
if (use_curses) {
mvaddstr(4, 0, "Press any key to quit ");
(void)getch();
endwin();
}
exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
The output without the -c
option (on Ubuntu 17.04 x86_64) is:
Press UP, DOWN, Escape, LEFT, RIGHT
Incorrect input: (-1, -1, -1, -1, -1)
UPDATE: A solution using something other than curses would be fine. It would have to use terminfo, directly or indirectly, to know what input sequences to look for. (It seems to me that recognizing special key inputs should be straightforward and not intimately tied to taking over the entire screen.)