在 OSX 下使用 termios 捕获 ESCAPE 键
Capture ESCAPE key using termios under OSX
我正在尝试在 OSX 终端上捕获 ESC 键 (ASCII 27) 或 xterm
使用 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;
}
但它只在第一次有效,第二次我按退出键时,终端不接受更多数据。它不会完全冻结,因为提示一直在闪烁,但按下更多键不会改变任何东西。
如果您使用 read
直接读取标准输入,那么像这样操作 VTIME
和 VMIN
会很有用。但是,您正在通过 C 流输入 stdin 读取它,这意味着您依赖于 that 的特定行为,而不是低-级 termios 功能。
程序循环,因为 getchar
已确定它已检测到文件结束条件,并继续 return -1,从不 returning 到 kb_hit
.您可以通过调用
来修改它
clearerr(stdin);
在调用 getchar
之后(因为它重置了文件结束条件),尽管依赖流和低级别 I/O 之间的任何特定行为或交互是不可移植的。
的 Linux 手册页
It is not advisable to mix calls to input functions from the stdio library with low-level calls to read(2)
for the file descriptor associated with the input stream; the results will be undefined and very probably not what you want.
供参考:
我正在尝试在 OSX 终端上捕获 ESC 键 (ASCII 27) 或 xterm
使用 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;
}
但它只在第一次有效,第二次我按退出键时,终端不接受更多数据。它不会完全冻结,因为提示一直在闪烁,但按下更多键不会改变任何东西。
如果您使用 read
直接读取标准输入,那么像这样操作 VTIME
和 VMIN
会很有用。但是,您正在通过 C 流输入 stdin 读取它,这意味着您依赖于 that 的特定行为,而不是低-级 termios 功能。
程序循环,因为 getchar
已确定它已检测到文件结束条件,并继续 return -1,从不 returning 到 kb_hit
.您可以通过调用
clearerr(stdin);
在调用 getchar
之后(因为它重置了文件结束条件),尽管依赖流和低级别 I/O 之间的任何特定行为或交互是不可移植的。
It is not advisable to mix calls to input functions from the stdio library with low-level calls to
read(2)
for the file descriptor associated with the input stream; the results will be undefined and very probably not what you want.
供参考: