【问题标题】:Capture ESCAPE key using termios under OSX在 OSX 下使用 termios 捕获 ESCAPE 键
【发布时间】:2016-03-16 15:55:36
【问题描述】:

我正在尝试在 OSX 终端或 xterm 上捕获 ESC 键(ASCII 27),使用 kbhit 来区分真正的 Escape 与箭头键:

#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;
}

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

【问题讨论】:

  • 在 Linux 上为我工作。你试过在调试器下运行它吗?
  • 谢谢@n.m.,是的,它在 Debian 上也适用于我,Mavericks 没有 gdb 也没有 valgrind,不是我的笔记本电脑,我无法安装它:(
  • 我听说在 OSX 上将 VMIN 恢复为 1 并不总是有效。 bitbucket.org/techtonik/python-pager/issues/5/…。不知道如何应对。

标签: c macos terminal xterm termios


【解决方案1】:

如果您直接使用read 读取标准输入,那么像这样操作VTIMEVMIN 会很有用。但是,您通过 C 流输入 stdin 读取它,这意味着您依赖于 that 的特定行为,而不是低级 termios 功能。

程序循环,因为getchar 已确定它已检测到文件结束条件,并继续返回-1,永远不会返回kb_hit。您可以通过调用来修改它

clearerr(stdin);

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

例如,getchargets 的 Linux 手册页建议

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

供参考:

【讨论】:

  • 谢谢Thomas,您的意思是最好使用read 而不是getchar? (无论过错)
  • 是的:如果不深入研究 OSX 的 getchar 的源代码,我无法确切地说 为什么 它设置了 EOF 条件。这些内部细节没有很好地标准化(并且可能会有所不同)。但是使用read,您可以使程序更加可预测和可移植。
  • read 是否将stdout 置于行缓冲模式?,当我使用read 而不是getchar() 时,我必须fflush(stdout);
  • read 不影响stdout。您的程序可能会混合其他可能相关的调用,例如 writeprintf
  • 再次感谢您,Thomas,抱歉,我没听懂您的意思,在从 getchar 切换到 read 之前,我能够在没有尾随换行符或致电 @987654351 的情况下使用 printf("something"); @。发生了一些变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-02
  • 2020-05-08
  • 1970-01-01
  • 2013-12-22
  • 2012-05-25
  • 1970-01-01
相关资源
最近更新 更多