终端程序(尤其是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;
}