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;
}