termios的例子

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章