GNU Readline:如何清除输入行?

我以“选择”方式使用GNU Readline,通过注册回调函数,如下所示:

rl_callback_handler_install("", on_readline_input); 

然后连接rl_callback_read_char作为STDIN_FILENO select()循环的回调。 这都是非常标准的东西,并且工作正常。

现在,我的程序异步地将消息打印到屏幕上,有时与用户的输入交错。 “干净”会话看起来像这样:

 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工具?

有空格? 尝试为要“删除”的每个字符打印"\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. 

经过大量的黑客攻击后,我得到了这种机制。 我希望其他人会发现它很有用。 它甚至不使用select(),但我希望你能明白这一点。

 #include  #include  #include  #include  #include  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(); } 

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

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

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

我试图用ncurses窗口分离服务器输出和用户输入。 使用线程模拟服务器输出。 程序将一直运行,直到您输入以“q”开头的行。

 #include  #include  #include  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(); } 

这些function有帮助吗?

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

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

这似乎也有效:

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