最简单的方法是将stdin更改为非阻塞模式。在Linux和其他系统上,可以使用fcntl()函数更改stdin上的F_SETFL选项来实现此操作。
这样,fgetc()(或任何其他读取)将不再阻塞并立即返回。如果没有任何可读内容(没有输入),它将返回错误。如果有内容,它将返回该字符。
以下是更改stdin为非阻塞模式所需的调用:
fcntl(0,F_SETFL,FNDELAY);
第一个参数是文件句柄。我们传入0,这是stdin。下一个参数是要更改的选项(在本例中为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);
}
这很棒。它不再阻塞,但仍会回显所键入的字符,并且在从
fgetc()
获取任何输入之前,需要按回车键。
如果我们想让它更像游戏,我们需要告诉终端停止干扰我们的输入。我们需要将终端切换到RAW模式。
以下示例将终端切换为RAW模式并将stdin更改为非阻塞模式。
#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;
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
atexit(ShutDownIO);
cfmakeraw(&new_termios);
new_termios.c_iflag|=INLCR;
new_termios.c_lflag|=ISIG;
tcsetattr(0, TCSANOW, &new_termios);
fcntl(0, F_SETFL, FNDELAY);
}
void ShutDownIO(void)
{
tcsetattr(0, TCSANOW, &orig_termios);
fcntl(0, F_SETFL, 0);
}
这样做的缺点是,如果您使用Ctrl-C或者程序崩溃,它会使您的终端处于混乱状态,很难恢复。您可以通过添加
atexit()
和/或信号处理来帮助解决这个问题。