GNU Readline:如何清除输入行?

时间:2009-10-02 22:08:16

标签: c 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工具?

6 个答案:

答案 0 :(得分:3)

有空格吗?尝试为您要“删除”的每个字符打印"\b \b",而不是单个'\b'


修改

工作原理
假设你写了“Hello,world!”到显示设备,你想要取代“世界!”与“吉姆。”

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.

答案 1 :(得分: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();
    }

答案 2 :(得分:1)

您可以做的一件事是使用\r跳转到服务器输出行的开头。然后,您可以使用字段宽度说明符将输出右键填充到行的其余部分。实际上,这将覆盖用户已输入的任何内容。

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

在执行此操作之前,您可能希望fflush(stdout)确保缓冲区处于一致状态。

答案 3 :(得分: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();
}

答案 4 :(得分:0)

这些功能中的任何一个都有帮助吗?

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

此外,您是否可以调解服务器输出 - 控制服务器输出,使其仅在您希望的时间和地点出现,而不是只是在用户输入的内容上蔓延?例如,如果您的应用程序在curses模式下运行,您是否可以在一个子窗口底部有一行或两行,用于用户输入,其余部分用于输出(服务器输出和接受的用户输入)它上面的第二个子窗口?

答案 5 :(得分:0)

这似乎也有效:

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