简体   繁体   English

GNU Readline:如何清除输入行?

[英]GNU Readline: how to clear the input line?

I use GNU Readline in the "select" fashion, by registering a callback function like so: 我通过注册回调函数,以“选择”的方式使用GNU Readline:

rl_callback_handler_install("", on_readline_input);

And then hooking up rl_callback_read_char as the callback for my select() loop for STDIN_FILENO . 然后将rl_callback_read_char挂钩作为STDIN_FILENO select()循环的回调。 That's all pretty standard stuff, and works fine. 这都是非常标准的东西,并且工作正常。

Now, my program asynchronously prints messages to the screen, sometimes interleaved with input from the user. 现在,我的程序异步地将消息打印到屏幕上,有时与用户的输入交错。 A "clean" session would look like this: “干净”的会话看起来像这样:

user input
SERVER OUTPUT
SERVER OUTPUT
user input
SERVER OUTPUT

But what if the user is midway through a line when the server response arrives? 但是,如果用户在服务器响应到达时位于线路中间,该怎么办? Then it gets ugly: 然后它变得丑陋:

user input
SERVER OUTPUT
user inSERVER OUTPUT
put
SERVER OUTPUT

I fixed this simply by printing a newline before the server output if the user had typed anything (this is easy to tell by checking rl_line_buffer ), and then doing rl_forced_update_display() after printing the server output. 我通过在服务器输出之前打印换行符来解决这个问题,如果用户输入了任何东西(这很容易通过检查rl_line_buffer来判断),然后在打印服务器输出后执行rl_forced_update_display() Now it looks like this: 现在它看起来像这样:

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

This is better, but still not perfect. 这更好,但仍然不完美。 The problem comes when the user typed an entire line but didn't yet press Enter--then it looks like this: 当用户键入整行但尚未按Enter键时出现问题 - 那么它看起来像这样:

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

This is bad because it appears to the user that they typed three commands (three responses for three inputs is just as possible as three responses for two inputs, which is what actually happened). 这很糟糕,因为用户看起来他们输入了三个命令(三个输入的三个响应与两个输入的三个响应一样可能,这实际上是发生的)。

A nasty hack (which works) is to do this: 讨厌的黑客(有效)是这样做的:

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

I figured I could improve this by printing backspace ('\\b') characters instead of " - INCOMPLETE" , but that doesn't seem to do anything at all on my terminal (gnome-terminal on Ubuntu Hardy). 我想我可以通过打印退格('\\ b')字符而不是" - INCOMPLETE"来改进这一点,但这似乎在我的终端上没有做任何事情(Ubuntu Hardy上的gnome-terminal)。 printf("ABC\\b"); just prints ABC , for whatever reason. 无论出于何种原因,只打印ABC

So how can I erase the incomplete input line? 那么如何擦除不完整的输入线? Either by printing backspaces somehow (I can figure out how many to print--it's strlen(rl_line_buffer) ), or by using some Readline facility I don't yet know about? 要么以某种方式打印退格(我可以弄清楚要打印多少 - 它是strlen(rl_line_buffer) ),还是使用一些我还不知道的Readline工具?

With spaces? 有空格? Try to print "\\b \\b" for each character you want to "delete" rather than a single '\\b' . 尝试为要“删除”的每个字符打印"\\b \\b" ”而不是单个'\\b'


Edit 编辑

How it works 这个怎么运作
Suppose you have written "Hello, world!" 假设你写了“Hello,world!” to the display device and you want to replace "world!" 到显示设备,你想要取代“世界!” with "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 可移植性
I'm not sure, but the Standard specifically describes the behaviour of '\\b' and '\\r' as has been described in answers to your question. 我不确定,但标准专门描述了'\\ b'和'\\ r'的行为,正如你的问题的答案中所描述的那样。

Section 5.2.2 Character display semantics 第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.

After quite a lot of hacking I was able to get this mechanism. 经过大量的黑客攻击后,我得到了这种机制。 I hope other people will find it useful. 我希望其他人会发现它很有用。 It does not even use select(), but I hope you will get the point. 它甚至不使用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();
    }

One thing you can do is to use \\r to jump to the beginning of the line for the server output. 您可以做的一件事是使用\\r跳转到服务器输出的行的开头。 Then you can use field width specifiers to right pad the output to the rest of the line. 然后,您可以使用字段宽度说明符将输出右键填充到行的其余部分。 This will, in effect, overwrite whatever the user had already entered. 实际上,这将覆盖用户已经输入的内容。

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

You may want to fflush(stdout) to ensure that the buffers are in a consistent state before you do that. 在执行此操作之前,您可能需要fflush(stdout)以确保缓冲区处于一致状态。

I tried to separate server output and user input with ncurses windows. 我试图用ncurses窗口分离服务器输出和用户输入。 Server output is simulated with a thread. 使用线程模拟服务器输出。 The program run until You enter a line beginning with 'q'. 程序将一直运行,直到您输入以“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();
}

Do any of these functions help? 这些功能有帮助吗?

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

Also, can you mediate the server output - have the server output controlled so that it only appears when and where you want it to, rather than just sprawling over what the user is typing? 此外,您可以调解服务器输出 - 控制服务器输出,使其仅在您希望的时间和地点出现,而不是只是在用户输入的内容上蔓延? For example, if your application is running in curses mode, could you have a split window with a line or two at the bottom in one sub-window reserved for user input and the rest of the output (server output and accepted user input) in a second sub-window above it? 例如,如果您的应用程序在curses模式下运行,您是否可以在一个子窗口底部有一行或两行,用于用户输入,其余部分用于输出(服务器输出和接受的用户输入)它上面的第二个子窗口?

This also seems to work: 这似乎也有效:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM