我正在尝试在
我的 BASIC 解释器中实现非阻塞键盘输入以支持
INKEY$
。阅读 SO 上的(很多!)线程,我发现了 POSIX 下的规范解决方案:
static int getkey(void)
{
int c;
static struct termios oldState, newState;
if (tcgetattr(STDIN_FILENO, &oldState) == -1)
return 0;
newState = oldState;
newState.c_lflag &= ~ICANON; // use noncanonical input
newState.c_lflag &= ~ECHO; // disable echo
newState.c_cc[VMIN] = 1; // minimum chars to wait for
newState.c_cc[VTIME] = CTIME; // minimum wait time
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newState) == -1)
return 0;
c = getchar();
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &oldState) == -1)
return 0;
return c;
}
当 newState.c_cc[VMIN] = 1 时,它会停止并等待,但否则会按预期工作。将其更改为 0 会阻止其等待,但也会导致 c 永远不会非零。我检查了 tcsetattr 是否失败并且返回 0 - 不,一切都很好。
有人真的可以在任何现代的 macOS 上使用这个功能吗?我使用的是 12.7.1 和 Xcode 14.2,但我怀疑这段代码是否发生了变化,因为按钮仍然是蓝色的并且在跳动。
附注与此相关的是,ECHO 标志似乎在 Xcode 控制台中没有效果,但在终端中按预期工作。我想这并不完全令人惊讶。
一些问题...
tcsetattr
once(使用 TCSANOW
)。VMIN
设置为 0,并将 VTIME
设置为 0。getchar
。不要将原始 tty 输入与 stdio
混合在一起。请使用 read
来代替。cfmakeraw
,可能需要进行一些额外的调整。这是一些示例代码:
void
ttyinit(int fd)
{
struct termios tio;
tcgetattr(fd,&tio);
cfmakeraw(&tio);
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = 0;
tcsetattr(fd,TCSANOW,&tio);
}
int
ttyget(int fd)
{
char buf[1];
int len;
len = read(fd,buf,1);
if (len > 0)
len = buf[0] & 0xFF;
return len;
}