在 curses 模式之外使用 getch 的可移植性如何?

How portable is the use of getch outside curses mode?

我正在开发一个终端程序来识别各个按键,包括键盘按键,但如果可能,我不想在 curses/program 模式下进行。我想我可能只是利用 curses 并使用 tcgetattr()tcsetattr() 在 curses 模式之外做我想做的事情,而不是使用 terminfo 和某种映射或树结构来重新发明轮子以进行快速键盘键匹配仍在使用 curses I/O 函数为我翻译键盘按键。令我惊讶的是,这有效 (Linux, ncurses 6.1.20180127):

/**
 * Most error checking elided for brevity.
 */
#include <stdio.h>     // printf
#include <string.h>    // memcpy

#include <curses.h>
#include <termios.h>   // tcgetattr, tcsetattr

int main(void)
{
    struct termios curr, new_shell_mode;
    int c, fd;
    SCREEN *sp;
    FILE *ttyf;

    /*
     * Initialize desired abilities in curses.
     * This unfortunately clears the screen, so
     * a refresh() is required, followed by
     * endwin().
     */
    ttyf = fopen("/dev/tty", "r+b");
    fd = fileno(ttyf);
    sp = newterm(NULL, ttyf, ttyf);
    raw();
    noecho();
    nonl();
    keypad(stdscr, TRUE);
    refresh();
    // Save the current curses mode TTY attributes for later use.
    tcgetattr(fd, &curr);
    endwin();

    /*
     * Set the shell/non-curses mode TTY attributes to
     * match those of program/curses mode (3 attempts).
     */
    memcpy(&new_shell_mode, &curr, sizeof curr);
    for (c = 0; c < 3; c++) {
        tcsetattr(fd, TCSADRAIN, &new_shell_mode);
        tcgetattr(fd, &curr);
        if (0 == memcmp(&new_shell_mode, &curr, sizeof curr))
            break;
    }
    // If new shell mode could fully be set, get a key press.
    if (c != 3)
        c = getch();
    reset_shell_mode();
    delscreen(sp);
    fclose(ttyf);
    printf("%02X\n", c);
    return 0;
}

但是,考虑到我已经退出 curses 模式,实际上 safe/portable 是否仍按所示方式使用 getch()

或者我是否需要采用更困难的方式使用 setupterm() 来加载 terminfo 数据库并循环遍历 strnames 数组,为每个数组调用 tigetstr(),然后设置我的手动拥有 termios 标志并自己处理读取按键?

XSI Curses 规范似乎没有禁止这样做,前提是 stdscr 仍然有效,这似乎是在程序退出或调用 delwin() 之前,我可以继续使用它,并且因为 stdscr 连接到我的 ttyf 文件,它是终端,我可以用它来获得按键,而无需自己处理所有事情。

您使用 newterm 初始化了 curses,但 无关紧要 你调用的 endwin:如果 curses 执行 refresh 作为副词,curses 将恢复全屏模式-调用 getch.

的效果

这不仅仅是 ncurses,还有任何 curses 实现(除了 1980 年代早已过时的 BSD 版本)。 X/Open Curses 笔记

If the current or specified window is not a pad, and it has been moved or modified since the last refresh operation, then it will be refreshed before another character is read.

在您的示例中,什么都没有 "moved or modified"。但是 getch 检查。 (endwin/termios 的东西可能没有任何好处,因为 newterm 没有明确说明-屏幕直到第一个 refresh).