在OSX下使用termios捕获ESCAPE密钥

时间:2015-12-10 20:59:41

标签: c macos terminal xterm termios

我试图在OSX终端上捕获ESC密钥(ASCII 27)或使用kbhit捕获xterm来区分真正的Escape和Arrow键:

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

static struct termios newt;
static struct termios oldt;

static void kb_fini(void)
{
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}

void kb_init(void)
{
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= (tcflag_t)~(ICANON | ECHO | ISIG);
    newt.c_cc[VMIN] = 1;
    newt.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    atexit(kb_fini);
}

static int kb_hit(void)
{
    int c = 0;

    newt.c_cc[VMIN] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    c = getc(stdin);
    newt.c_cc[VMIN] = 1;
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    if (c != -1) {
        ungetc(c, stdin);
        return 1;
    }
    return 0;
}

int main(void)
{
    int c;

    kb_init();
    printf("Press ESC several times\n");
    while (1) {
        c = getchar();
        if ((c == 27) && (kb_hit() == 0)) {
            printf("You pressed ESC\n");    
        } else
        if (c == '\n') {
            break;
        }
    }
    return 0;
}

但它只在第一次工作时,第二次按下转义键,终端不接受更多数据。它不会完全冻结,因为提示会一直闪烁,但按下更多键不会改变任何内容。

1 个答案:

答案 0 :(得分:2)

如果您使用VTIME直接阅读标准输入,那么操作VMINread会非常有用。但是,您正在通过C流输入 stdin 读取它,这意味着您依赖于 的特定行为,而不是低级别的termios功能。< / p>

程序循环,因为getchar已经确定它已检测到文件结束条件,并继续返回-1,从不返回kb_hit。你可以通过调用

来修改它
clearerr(stdin);

调用getchar后(因为它重置了文件结束条件),但依赖于任何特定行为或流与低级I / O之间的交互是不可移植的。

例如,getchargets的Linux手册页建议

  

对于与输入流关联的文件描述符,不建议将对stdio库的输入函数调用与 read(2) 的低级调用混合使用;结果将是未定义的,很可能不是你想要的。

供参考: