最简单的方法就是将标准输入更改为非阻塞。这是通过使用fcntl() 函数更改标准输入上的F_SETFL 选项来完成的(在Linux 和其他系统上)。
这将使fgetc()(或任何其他读取)不再阻止立即返回。如果没有任何东西要读取(没有输入),它将返回一个错误。如果有东西,它会返回那个字符。
这是将标准输入更改为非阻塞所需的调用。
fcntl(0, F_SETFL, FNDELAY);
第一个参数是文件句柄。我们传入 0,即标准输入。下一个参数是我们要更改的选项(在本例中为 F_SETFL),第三个参数是要更改的内容(FNDELAY 是非阻塞模式)。
这是一个简单的示例程序:
#include <stdio.h>
#include <fcntl.h>
void SetupIO(void);
void ShutDownIO(void);
int main(void)
{
long count;
char c;
SetupIO();
count=0;
for(;;)
{
printf("Counting %d\r",count);
count++;
c=fgetc(stdin);
if(c=='q')
break;
}
ShutDownIO();
return 0;
}
void SetupIO(void)
{
fcntl(0, F_SETFL, FNDELAY);
}
void ShutDownIO(void)
{
fcntl(0, F_SETFL, 0);
}
这很棒。它不再阻塞,但仍会回显正在输入的字符,您需要按 Enter 键才能从 fgetc() 取回任何输入。
如果我们想让它更像游戏,我们需要告诉终端停止干扰我们的输入。我们需要将终端切换到 RAW 模式。
以下示例将终端转为 RAW 模式并将标准输入更改为非阻塞。
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
void SetupIO(void);
void ShutDownIO(void);
struct termios orig_termios;
int main(void)
{
long count;
char c;
SetupIO();
count=0;
for(;;)
{
printf("Counting %d\r",count);
count++;
c=fgetc(stdin);
if(c=='q')
break;
}
ShutDownIO();
return 0;
}
void SetupIO(void)
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(ShutDownIO);
cfmakeraw(&new_termios);
new_termios.c_iflag|=INLCR; // CR=NL
new_termios.c_lflag|=ISIG; // We still want Ctrl-C
tcsetattr(0, TCSANOW, &new_termios);
/* Non-blocking */
fcntl(0, F_SETFL, FNDELAY);
}
void ShutDownIO(void)
{
tcsetattr(0, TCSANOW, &orig_termios);
fcntl(0, F_SETFL, 0);
}
这样做的缺点是,如果您 Ctrl-C 或您的程序崩溃,它将使您的终端处于混乱状态,不容易退出。您可以通过添加 atexit() 和/或信号处理来帮助解决此问题。