简体   繁体   中英

Recognizing special keys without taking over the screen

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

The filter function, called before initscr , tells curses to use a single line (rather than the whole screen). There is an example of its use (named "filter") in ncurses-examples , which consists of a command-prompt.

getch updates at least part of the screen, eg, when it echoes the input. That update can affect either just a line (using filter ), or the whole screen. If you're clever, you can turn off echo, and (since filter suppresses the screen erasure), pretend that it updates nothing at all. For example, you could redirect the output to /dev/null by initializing curses with newterm .

Echoing input is only part of the story: curses does initialize the screen, and the associated refresh done by getch would make that hard to avoid in a portable way. There's an ncurses-specific option in the sample program which works around unwanted switching between normal/alternate screens, which could occur during screen-initialization.

Here's a screenshot of "filter", demonstrating that it does not take over the whole screen. The inverse video is due to ncurses initializing colors (white-on-black), but as the help-message indicates, that's configurable. Without making a movie, it's hard to demonstrate that it handles special keys, but reading the source code should be enough.

过滤程序示例

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM