2009-10-02 98 views
5

我使用GNU Readline庫中的「選擇」的方式,通過註冊一個回調函數,像這樣:GNU Readline:如何清除輸入行?

rl_callback_handler_install("", on_readline_input); 

然後鉤住rl_callback_read_char作爲回調我select()環路STDIN_FILENO。這都是非常標準的東西,並且工作正常。

現在,我的程序異步地將消息輸出到屏幕,有時與來自用戶的輸入交錯。 「乾淨」的會議將是這樣的:

user input 
SERVER OUTPUT 
SERVER OUTPUT 
user input 
SERVER OUTPUT 

但是,如果用戶中途當服務器響應到達的線路呢?然後它得到醜陋:

user input 
SERVER OUTPUT 
user inSERVER OUTPUT 
put 
SERVER OUTPUT 

我解決了這個問題通過簡單的打印服務器輸出前一個換行符,如果用戶輸入了什麼(這是很容易通過檢查rl_line_buffer告訴),然後打印服務器輸出後做rl_forced_update_display() 。現在看起來像這樣:

user input 
SERVER OUTPUT 
user in 
SERVER OUTPUT 
user input 
SERVER OUTPUT 

這樣比較好,但還不完美。問題是當用戶輸入一整行但還沒有按Enter鍵 - 那麼它看起來是這樣的:

user input 
SERVER OUTPUT 
user input 
SERVER OUTPUT 
user input 
SERVER OUTPUT 

這是不好的,因爲它在用戶看來,他們鍵入的三個命令(用於三個響應三個輸入就像三個輸入的響應一樣,這是實際發生的情況)。

一個討厭的黑客(工作)是這樣做:

user input 
SERVER OUTPUT 
user input - INCOMPLETE 
SERVER OUTPUT 
user input 
SERVER OUTPUT 

我想我可以通過打印退格(「\ B」)字符,而不是" - INCOMPLETE"改善這一點,但似乎沒有不在我的終端上執行任何操作(Ubuntu Hardy上的gnome-terminal)。無論出於何種原因,printf("ABC\b");只是打印ABC

那麼如何擦除不完整的輸入行?要麼通過打印退格空間(我可以計算出打印數量 - 它是strlen(rl_line_buffer)),還是通過使用一些我不知道的Readline工具?

回答

3

With spaces?嘗試打印"\b \b"爲每個你想要「刪除」的字符,而不是一個'\b'


編輯

它是如何工作
假設你已經寫了 「你好,世界!」到顯示設備,你想要取代「世界!」與「吉姆」。

Hello, world! 
      ^/* active position */ /* now write "\b \b" */ 
       /* '\b' moves the active position back; 
       // ' ' writes a space (erases the '!') 
       // and another '\b' to go back again */ 
Hello, world 
      ^/* active position */ /* now write "\b \b" again */ 
Hello, worl 
     ^/* active position */ /* now write "\b \b" 4 times ... */ 
Hello, 
    ^/* active position */ /* now write "Jim." */ 
Hello, Jim. 
     ^/* active position */ 

便攜
我不知道,但標準明確描述的「\ B」和已經在答覆中說明你的問題「\ r」的行爲。

5.2.2節字符顯示語義

> 1 The active position is that location on a display device where the next character output by 
>  the fputc function would appear. The intent of writing a printing character (as defined 
>  by the isprint function) to a display device is to display a graphic representation of 
>  that character at the active position and then advance the active position to the next 
>  position on the current line. The direction of writing is locale-specific. If the active 
>  position is at the final position of a line (if there is one), the behavior of the display devic e 
>  is unspecified. 
> 
> 2 Alphabetic escape sequences representing nongraphic characters in the execution 
>  character set are intended to produce actions on display devices as follows: 
>  \a (alert) Produces an audible or visible alert without changing the active position. 
>  \b (backspace) Moves the active position to the previous position on the current line. If 
>  the active position is at the initial position of a line, the behavior of the display 
>  device is unspecified. 
>  \f (form feed) Moves the active position to the initial position at the start of the next 
>  logical page. 
>  \n (new line) Moves the active position to the initial position of the next line. 
>  \r (carriage return) Moves the active position to the initial position of the current line. 
>  \t (horizontal tab) Moves the active position to the next horizontal tabulation position 
>  on the current line. If the active position is at or past the last defined horizontal 
>  tabulation position, the behavior of the display device is unspecified. 
>  \v (vertical tab) Moves the active position to the initial position of the next vertical 
>   tabulation position. If the active position is at or past the last defined vertical 
>   tabulation position, the behavior of the display device is unspecified. 
> 
> 3 Each of these escape sequences shall produce a unique implementation-defined value 
>  which can be stored in a single char object. The external representations in a text file 
>  need not be identical to the internal representations, and are outside the scope of this 
>  International Standard. 
+0

某些終端/終端仿真器具有退格符不同的行爲。 pmg有正確的想法。 – 2009-10-02 22:20:16

+0

這是在我的終端工作。但是,如果沒有更好的理解它爲什麼起作用以及它如何移動(不可移植),我選擇使用'\ r'(正如另一個答案中所建議的那樣)。 – 2009-10-05 16:35:25

+0

使用'\ r'代碼一段時間後我改變了主意......我更喜歡這樣做,因爲它不需要在末尾使用空格套印(我更喜歡在服務器輸出之前進行套印,所以這個解決方案最適合我,更不用說它是我最初的問題最直接的答案)。 – 2009-10-12 14:59:53

0

我試圖分開服務器輸出和用戶輸入與ncurses的窗口。服務器輸出用線程模擬。程序一直運行到您輸入以'q'開頭的行。

#include <unistd.h> 
#include <curses.h> 
#include <pthread.h> 

WINDOW *top, *bottom; 

int win_update(WINDOW *win, void *data){ 
    wprintw(win,"%s", (char*)data); wrefresh(win); 
    return 0; 
} 

void *top_thread(void *data){ 
    char buff[1024]; 
    int i=0; 
    while(1){ 
    snprintf(buff, 1024, "SERVER OUTPUT: %i\n", i++); 
    use_window(top, win_update, (void*)buff); 
    sleep(1); 
    } 
    return NULL; 
} 

int main(){ 
    initscr(); 
    int maxy, maxx; 
    getmaxyx(stdscr, maxy, maxx); 

    top = newwin(maxy-1,maxx,0,0); 
    wsetscrreg(top,0,maxy-1); idlok(top,1); scrollok(top,1); 
    pthread_t top_tid; 
    pthread_create(&top_tid, NULL, top_thread, NULL); 

    bottom = newwin(1,maxx,maxy-1,0); 
    char buff[1024], input[maxx]; 
    do{ 
    werase(bottom); wmove(bottom,0,0); 
    wprintw(bottom,"input> "); wrefresh(bottom); 
    wgetnstr(bottom,input,sizeof(input)); 
    snprintf(buff, 1024, "user input: '%s'\n", input); 
    use_window(top, win_update, (void*)buff); 
    }while(input[0] != 'q'); 

    endwin(); 
} 
+0

不幸的是,curses和readline不能混用。請參閱http://stackoverflow.com/questions/691652/using-gnu-readline-how-can-i-add-ncurses-in-the-same-program(也由我問)。 – 2009-10-03 18:48:30

1

一兩件事你可以做的是使用\r跳轉到服務器輸出的行的開頭。然後,您可以使用字段寬度說明符將輸出填充到該行的其餘部分。實際上,這將覆蓋用戶已經輸入的任何內容。

fprintf (stdout, "\r%-20s\n", "SERVER OUTPUT"); 

您可能希望fflush(stdout),以確保緩衝區是一致的狀態你這樣做之前。

+0

這很聰明,我實施它,並使用它一段時間。最後我發現我更喜歡pmg發佈的「\ b \ b」解決方案。但是,好的! – 2009-10-12 15:00:31

0

做這些功能有幫助嗎?

  • rl_reset_line_state()
  • rl_clear_message()
  • rl_delete_text()
  • rl_kill_text()

此外,您還可以調解服務器輸出 - 已在服務器輸出控制,使得只出現在何時何地你想要它到,而不是僅僅蔓延到用戶輸入的內容?例如,如果您的應用程序以curses模式運行,您是否可以在一個爲用戶輸入保留的子窗口底部有一行或兩行的分割窗口,以及輸入的其餘部分(服務器輸出和接受的用戶輸入)它上面的第二個子窗口?

+0

如果你建議我讓我的readline應用程序也是一個curses應用程序,這似乎是不可能的:http://stackoverflow.com/questions/691652/using-gnu-readline-how-can-i-add-ncurses在同一個程序 – 2009-10-03 18:49:43

+0

我試過'rl_reset_line_state()'和'rl_clear_message()'...這些都沒有幫助。我會嘗試一些更多的readline函數,但我想我已經經歷了很多有趣的事情。 – 2009-10-03 18:51:21

+0

@John Zwinck:我還沒有推動readline庫足夠的努力去知道這些是否有用。如果readline不能用於curses(不是一個大的驚喜),那麼有兩種可能性:(1)忽略該建議,或者(2)修改應用程序以使用curses而不是readline。那(選項2)肯定是更多的工作。 – 2009-10-03 20:00:29

3

經過相當多的黑客攻擊之後,我纔得到了這個機制。我希望其他人會覺得它有用。它甚至不使用select(),但我希望你能明白這一點。

#include <readline/readline.h> 
    #include <readline/history.h> 
    #include <stdio.h> 
    #include <unistd.h> 
    #include <stdlib.h> 

    const char const* prompt = "PROMPT> "; 

    void printlog(int c) { 
     char* saved_line; 
     int saved_point; 
     saved_point = rl_point; 
     saved_line = rl_copy_text(0, rl_end); 
     rl_set_prompt(""); 
     rl_replace_line("", 0); 
     rl_redisplay(); 
     printf("Message: %d\n", c); 
     rl_set_prompt(prompt); 
     rl_replace_line(saved_line, 0); 
     rl_point = saved_point; 
     rl_redisplay(); 
     free(saved_line); 
    } 


    void handle_line(char* ch) { 
     printf("%s\n", ch); 
     add_history(ch); 
    } 

    int main() { 
     int c = 1; 

     printf("Start.\n"); 
     rl_callback_handler_install(prompt, handle_line); 

     while (1) { 
      if (((++c) % 5) == 0) { 
       printlog(c); 
      } 

      usleep(10); 
      rl_callback_read_char(); 
     } 
     rl_callback_handler_remove(); 
    } 
+0

有關工作示例,請參閱http://github.com/dpc/xmppconsole/blob/master/src/io.c。 – 2010-06-09 19:00:52

+0

這應該是公認的解決方案。它可能很古怪,但它應該正確處理多行輸入。 – 2015-04-07 01:05:35

0

這也似乎工作:

rl_clear_visible_line(); 
printf(...); 
rl_reset_line_state(); 
rl_redisplay();