区分Esc和ANSI终端上的其他特殊键

时间:2018-10-28 09:58:11

标签: unix terminal ansi-escape

终端程序(尤其是vi和vim)可以区分Esc(转义)键和其他特殊键(例如箭头键),它们具有不同的命令。

在终端仿真器中,Esc似乎仅使用单个十六进制字节进行编码:

{ 1b }

但是,某些特殊键(箭头键,向上/向下翻页,home / end,插入/删除)是从同一字节开始的多字节序列:

{ 1b 5b 41 } up
{ 1b 5b 42 } down
{ 1b 5b 43 } right
{ 1b 5b 44 } left

鉴于此字节的双重作用,我如何检测用户何时按下了转义键,而不是按下了其他键之一?

  • 我不能等待0x1b之后的下一个字节,因为可能没有一个。用户可以按Escape键,然后无限期地等待我的程序响应该键。

  • 我可以调用一次read()来读取终端上所有可用的数据,并检查它是否只有一个字节或几个字节。只要我们相信终端足够快地及时发送read()的所有相关字节,并且一次只发送一次击键(这样read()不会返回额外的数据或部分击键)。如今,这是人们实际上总是使用终端仿真器的合理假设吗?造成延迟的网络链接(例如SSH)如何处理?

  • 我可以反复调用read(),每次read()调用仅读取一个字节,直到没有更多可用字节为止。 linenoise库这样做是因为它与慢速终端的兼容性是其原因(但没有说明那些终端是哪个)。

一些一般注意事项:

  • 我应该进行阻塞读取还是非阻塞读取?有关系吗?

  • 我应该使用读取超时吗?如果是,那么是否经常使用一些凭经验确定的值?

我使用以下测试程序来显示按键的字节:

#include <sys/types.h>
#include <unistd.h>

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

static void dump(void) {
    unsigned char bytes[8];
    ssize_t i, n;
    while ((n = read(0, bytes, sizeof(bytes))) > 0) {
        printf("{ ");
        for (i = 0; i < n; i++) {
            printf("%02x ", bytes[i]);
        }
        printf("}\r\n");
        if (bytes[0] == 'q') {
            break;
        }
    }
}

int main(void) {
    struct termios origMode;
    struct termios rawMode;
    cfmakeraw(&rawMode);
    tcgetattr(0, &origMode);
    tcsetattr(0, TCSAFLUSH, &rawMode);
    dump();
    tcsetattr(0, TCSAFLUSH, &origMode);
    return 0;
}

0 个答案:

没有答案