linux程序设计 5.4节
先看一个menu.c函数
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
char *menu[] = {"a - add new recode", "b - delete recode", "q - quit", "NULL"};
int getchoice(char *greet, char *choices[], FILE *in, FILE *out);
int main()
{
int choice = 0;
FILE *in; //两个文件描述符,用来连接到终端(将终端作为文件进行操作)
FILE *out;
if( !isatty(fileno(stdout)) ) { //判断输出流是否连接到终端
fprintf(stderr, "You not a terminal! \n");
}
in = fopen("/dev/tty", "r"); //打开连接到终端的读描述符
out = fopen("/dev/tty", "w"); //打开连接到终端的写描述符
if( !in || ! out){
fprintf(stderr, "Unable to open .dev/tty \n");
return -1;
}
do{
choice = getchoice("Please select an action", menu, in , out);
printf("You have chosen: %c \n\n", choice);
}while( 'q' != choice );
return 0;
}
int getchoice(char *greet, char *choices[], FILE *in, FILE *out)
{
int chosen = 0;
int selected;
char **option;
do {
fprintf(out, "choice: %s \n", greet);
option = choices;
while( *option )
{
fprintf(out, "%s \n", *option);
option++;
}
fprintf(out, " \n");
do {
selected = fgetc(in);
}while('\n' == selected ); //标准输入模式下,回车符与换行符是相关联的,因此只需要检测其中任意一个即可
option = choices;
while(*option)
{
if( *option[0] == selected ){
chosen = 1;
break;
}
option++;
}
if( 1 != chosen ) {
fprintf(out, "Incorrect choice, select again \n\n");
}
}while(!chosen);
return selected;
}
该函数在标准输入模式下运行,当用户输入选项后需要键入回车,程序才会读取输入字符
要实现输入选项后,程序立即执行而不必等待回车的模式,我们可以对程序做以下修改
#include <unistd.h>
#include <stdio.h>
#include <termios.h> //包含termios结构和相关函数的头文件
char *menu[] = {"a - add new recode", "b - delete recode", "q - quit", "NULL"};
int getchoice(char *greet, char *choices[], FILE *in, FILE *out);
int main()
{
int choice = 0;
FILE *in;
FILE *out;
struct termios initial_settings, new_settings; //声明termios类型的结构体变量
if( !isatty(fileno(stdout)) ){
fprintf(stderr, "You not a terminal! \n");
}
in = fopen("/dev/tty", "r");
out = fopen("/dev/tty", "w");
if( !in || ! out){
fprintf(stderr, "Unable to open .dev/tty \n");
return -1;
}
tcgetattr(fileno(in), &initial_settings); //获得默认的终端属性
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON; //清除变量中由ICANON和ECHO标志定义的比特,将输入模式改为非标准模式
new_settings.c_lflag &= ~ECHO;
new_settings.c_cc[VMIN] = 1; //MIN = 1, TIME = 0时,read()在有一个字符等待处理时就调用
new_settings.c_cc[VTIME] = 0;
if ( 0 != tcsetattr(fileno(in), TCSANOW, &new_settings)){ //使新的属性生效
fprintf(stderr, "Could not set attributes \n");
}
do{
choice = getchoice("Please select an action", menu, in , out);
printf("You have chosen: %c \n\n", choice);
}while( 'q' != choice );
tcsetattr(fileno(in), TCSANOW, &initial_settings); //程序结束后,要记得恢复到原属性
return 0;
}
int getchoice(char *greet, char *choices[], FILE *in, FILE *out)
{
int chosen = 0;
int selected;
char **option;
do{
fprintf(out, "choice: %s \n", greet);
option = choices;
while( *option )
{
fprintf(out, "%s \n", *option);
option++;
}
fprintf(out, " \n");
do{
selected = fgetc(in);
}while('\n' == selected || '\r' == selected); //非标准模式下,回车符和换行符之间的映射已经不存在,因此需要分别检测
option = choices;
while(*option)
{
if( *option[0] == selected ){
chosen = 1;
break;
}
option++;
}
if( 1 != chosen ){
fprintf(out, "Incorrect choice, select again \n\n");
}
}while(!chosen);
return selected;
}