2016-06-26 19 views
3

我試圖檢測是否有數據標準輸入框讓我讀。選擇(2)和ioctl(2)返回0,而標準輸入有數據

具體而言,我已經使用tcsetattr關閉了規範模式,所以我可以一次讀取一個字符(阻止)。我想要檢測像箭頭鍵生成的轉義序列,但將它們與單獨的轉義鍵區分開來。另外,我想快速知道輸入的內容;我假設我的終端相當快,並且在^[之後的其他轉義序列相當快。

假設我已經知道(例如使用select(2))在標準輸入框中需要讀取內容。我使用getchar()讀取了一個字符,它是一個^[,ASCII 27。現在我想知道是否有更多內容在某個時間間隔內進入,或者此轉義字符是所有內容(這將指示轉義鍵的命中)。使用select(2)這似乎不工作,因爲(我注意到,並在其他地方讀)其餘的字符已經被緩衝,所以select(2)已經沒有什麼可以檢測了。所以我轉向ioctl(2)使用FIONREAD,但這似乎並不奏效。

最小(非)工作實施例:

#include <stdio.h> 
#include <stdlib.h> 
#include <termios.h> 
#include <sys/select.h> 
#include <sys/ioctl.h> 
#include <assert.h> 

struct termios tios_bak; 

void initkeyboard(void){ 
    struct termios tios; 
    tcgetattr(0,&tios_bak); 
    tios=tios_bak; 

    tios.c_lflag&=~ICANON; 
    tios.c_cc[VMIN]=1; // Read one char at a time 
    tios.c_cc[VTIME]=0; // No timeout on reading, make it a blocking read 

    tcsetattr(0,TCSAFLUSH,&tios); 
} 

void endkeyboard(void){ 
    tcsetattr(0,TCSAFLUSH,&tios_bak); 
} 

int main(void){ 
    initkeyboard(); 
    atexit(endkeyboard); 

    printf("Press an arrow key or the escape key, or the escape key followed by something else.\n"); 

    char c=getchar(); 
    if(c!=27){ 
     printf("Please input an escape sequence or key\n"); 
     exit(1); 
    } 

    // Now we use select(2) to determine whether there's anything more to read. 
    // If it was a lone escape key, there won't be anything new in a while. 
    fd_set rdset; 
    FD_ZERO(&rdset); 
    FD_SET(0,&rdset); 
    struct timeval tv; 
    tv.tv_sec=1; // Here we wait one second; this is just to illustrate. In a real environment 
    tv.tv_usec=0; // I'd wait something like 100ms, since that's reasonable for a terminal. 
    int ret=select(1,&rdset,NULL,NULL,&tv); 

    assert(ret!=-1); // (Error checking basically omitted) 
    if(ret==0){ 
     printf("select(2) returned 0.\n"); 
     int n; 
     assert(ioctl(0,FIONREAD,&n)>=0); 
     assert(n>=0); 
     if(n==0){ 
      printf("ioctl(2) gave 0; nothing to read: lone escape key\n"); 
      // INSERT printf("%c\n",getchar()); HERE TO DEMONSTRATE THIS IS WRONG IN CASE OF ESCAPE SEQUENCE 
     } else { 
      c=getchar(); 
      printf("ioctl(2) says %d bytes in read buffer (first char=%c)\n",n,c); 
     } 
    } else { 
     c=getchar(); 
     printf("select(2) returned %d: there was more to read (first char=%c)\n",ret,c); 
    } 
} 

很抱歉的長碼。會發生什麼如下:

  1. 當您只需按下轉義鍵,它會成功檢測到。
  2. 當你按下轉義鍵,然後快速的一些其他鍵(如'a'),代碼成功檢測到有更多的閱讀,這是好的;特定的轉義序列檢測超出了範圍。
  3. 當您生成轉義序列時,例如通過按箭頭鍵,select(2)ioctl(2)返回沒有什麼可讀,雖然顯然有;這可以通過在指定位置插入printf("%c\n",getchar());來輕鬆檢查。這將打印[(至少在箭頭鍵的情況下)。

問題:如何正確檢測輸入情況(3)?

回答

2

getchar手冊頁:

這是不可取的輸入函數的調用從低層次的 stdio庫混合調用,讀取(2)與輸入相關聯的文件描述符 流;結果將是未定義的 ,很可能不是你想要的。

不要混合緩衝和非緩衝輸入功能。

select必須結合使用

read(fileno(stdin), &c, 1); 

,而不是

c = getchar(); 
+1

謝謝,這就是它!還沒有看到這個需求在別的地方解釋過。 – tomsmeding

+1

@tomsmeding,編輯 –