2009-10-02 31 views
5

tôi sử dụng GNU Readline trong "chọn" thời trang, bằng cách đăng ký một hàm callback như vậy:GNU Readline: làm thế nào để xóa dòng đầu vào?

rl_callback_handler_install("", on_readline_input); 

Và sau đó hooking lên rl_callback_read_char như gọi lại cho select() vòng lặp của tôi cho STDIN_FILENO. Đó là tất cả những thứ khá chuẩn, và hoạt động tốt.

Hiện tại, chương trình của tôi không đồng bộ in các tin nhắn lên màn hình, đôi khi xen kẽ với đầu vào từ người dùng. Phiên "sạch" sẽ trông như sau:

user input 
SERVER OUTPUT 
SERVER OUTPUT 
user input 
SERVER OUTPUT 

Nhưng nếu người dùng đang ở giữa đường khi máy chủ phản hồi thì sao? Sau đó nó được xấu xí:

user input 
SERVER OUTPUT 
user inSERVER OUTPUT 
put 
SERVER OUTPUT 

tôi cố định này chỉ đơn giản bằng cách in một dòng mới trước khi sản lượng máy chủ nếu người dùng đã gõ bất cứ điều gì (điều này rất dễ dàng để nói bằng cách kiểm tra rl_line_buffer), và sau đó làm rl_forced_update_display() sau khi in ra máy chủ . Bây giờ nó trông như thế này:

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

Điều này tốt hơn, nhưng vẫn chưa hoàn hảo. vấn đề này được đưa ra khi người dùng gõ toàn bộ một dòng nhưng vẫn chưa nhấn Enter - sau đó nó trông như thế này:

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

này là xấu bởi vì nó dường như người dùng rằng họ đã gõ ba lệnh (ba câu trả lời cho ba đầu vào chỉ có thể là ba đáp ứng cho hai đầu vào, đó là những gì thực sự đã xảy ra).

Một hack khó chịu (có công trình) là để làm điều này:

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

I figured tôi có thể cải thiện điều này bằng cách in BackSpace ký tự thay vì " - INCOMPLETE" ('\ b'), nhưng điều đó dường như không làm bất cứ điều gì ở tất cả trên thiết bị đầu cuối của tôi (gnome-terminal trên Ubuntu Hardy). printf("ABC\b"); chỉ in ABC, vì bất kỳ lý do gì.

Vậy làm cách nào tôi có thể xóa dòng nhập không đầy đủ? Hoặc bằng cách in backspaces bằng cách nào đó (tôi có thể tìm ra bao nhiêu để in - đó là strlen(rl_line_buffer)), hoặc bằng cách sử dụng một số cơ sở Readline tôi chưa biết về?

Trả lời

3

Với khoảng trắng? Hãy thử in "\b \b" cho mỗi ký tự bạn muốn "xóa" thay vì một đơn '\b'.


Sửa

Làm thế nào nó hoạt động
Giả sử bạn đã viết "Hello, world!" với thiết bị hiển thị và bạn muốn thay thế "thế giới!" với "Jim".

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 */ 

Portability
Tôi không chắc chắn, nhưng các tiêu chuẩn mô tả cụ thể các hành vi của '\ r' như đã được mô tả trong câu trả lời cho câu hỏi của bạn '\ b' và.

Mục 5.2.2 Character hiển thị ngữ nghĩa

> 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

Một số thiết bị đầu cuối/giả lập thiết bị đầu cuối có hành vi khác nhau cho các nhân vật backspace. pmg có ý tưởng đúng. –

+0

Tính năng này hoạt động trong thiết bị đầu cuối của tôi. Nhưng nếu không có một sự hiểu biết tốt hơn về lý do tại sao nó hoạt động và làm thế nào (un) di động nó được, tôi đã chọn để sử dụng '\ r' thay thế (như đề xuất trong một câu trả lời). –

+0

Thay đổi ý định của tôi sau khi sử dụng mã '\ r' trong một thời gian ... Tôi làm như thế này tốt hơn, bởi vì nó không yêu cầu in đè lên không gian ở cuối (tôi thích làm quá mức trước khi đầu ra máy chủ, vì vậy giải pháp phù hợp với tôi nhất, chưa kể rằng đó là câu trả lời trực tiếp nhất cho câu hỏi ban đầu của tôi). –

0

tôi đã cố gắng để tách sản lượng máy chủ và người dùng nhập vào với ncurses cửa sổ. Đầu ra của máy chủ được mô phỏng bằng một luồng. Chương trình chạy cho đến khi Bạn nhập một dòng bắt đầu bằng '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

Thật không may, lời nguyền và đọc không pha trộn. Xem http://stackoverflow.com/questions/691652/using-gnu-readline-how-can-i-add-ncurses-in-the-same-program (cũng được tôi hỏi). –

1

Một điều bạn có thể làm là sử dụng \r để chuyển đến đầu dòng cho đầu ra máy chủ. Sau đó, bạn có thể sử dụng các specifier chiều rộng trường để pad phải đầu ra cho phần còn lại của dòng. Điều này sẽ, có hiệu lực, ghi đè lên bất cứ điều gì người dùng đã nhập.

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

Bạn có thể muốn fflush(stdout) để đảm bảo rằng bộ đệm ở trạng thái nhất quán trước khi bạn làm điều đó.

+0

Điều này là thông minh và tôi đã thực hiện nó và sử dụng nó trong một thời gian. Cuối cùng tôi thấy tôi thích giải pháp "\ b \ b" được đăng bởi pmg. Nhưng, tốt nhất! –

0

Có bất kỳ chức năng nào trong số này giúp ích không?

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

Ngoài ra, bạn có thể hòa giải đầu ra máy chủ - đã đầu ra máy chủ điều khiển để nó chỉ xuất hiện khi nào và nơi bạn muốn nó đến, thay vì chỉ nằm ngang trên những gì người dùng đang nhập? Ví dụ, nếu ứng dụng của bạn đang chạy trong chế độ curses, bạn có thể có một cửa sổ tách với một hoặc hai dòng ở dưới cùng trong một cửa sổ phụ dành riêng cho đầu vào của người dùng và phần đầu ra còn lại (đầu ra máy chủ và đầu vào người dùng được chấp nhận) trong một cửa sổ phụ thứ hai phía trên nó?

+0

Nếu bạn đang đề xuất rằng tôi làm cho ứng dụng readline của tôi cũng là một ứng dụng curses, nó có vẻ là không thể: http://stackoverflow.com/questions/691652/using-gnu-readline-how-can-i-add-ncurses -in-the-same-program –

+0

Tôi đã thử 'rl_reset_line_state()' và 'rl_clear_message()' ... cả hai đều không hữu ích. Tôi sẽ thử một số chức năng readline hơn khi có thể, nhưng tôi nghĩ rằng tôi đã trải qua khá nhiều tính năng thú vị. –

+0

@ John Zwinck: Tôi đã không đẩy thư viện readline đủ mạnh để biết liệu có bất kỳ thư viện nào hữu ích hay không. Và nếu readline sẽ không làm việc với lời nguyền (không phải là một bất ngờ lớn), thì có hai khả năng: (1) bỏ qua gợi ý, hoặc (2) sửa lại ứng dụng để sử dụng lời nguyền thay vì readline. Điều đó (tùy chọn 2) chắc chắn là công việc nhiều hơn. –

3

Sau khá nhiều hack tôi đã có thể nhận được cơ chế này. Tôi hy vọng những người khác sẽ thấy nó hữu ích. Nó thậm chí không sử dụng select(), nhưng tôi hy vọng bạn sẽ nhận được điểm.

#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

Xem http://github.com/dpc/xmppconsole/blob/master/src/io.c để biết ví dụ làm việc. –

+0

Đây phải là giải pháp được chấp nhận. Nó có thể là kỳ quặc, nhưng nó sẽ xử lý đầu vào đa dòng đúng cách. –

0

cũng Điều này dường như làm việc:

rl_clear_visible_line(); 
printf(...); 
rl_reset_line_state(); 
rl_redisplay(); 
Các vấn đề liên quan