GNU Readline (libreadline): Displaying output message asynchronously

99封情书 提交于 2021-02-08 06:32:13

问题


While using readline (blocking) for user input, I would like to output lines of text to the console asynchronously from another thread. Further, I would like that the readline prompt and current partial input line be removed from the console, the output line written, then the readline prompt and the partial user line restored - so as to give the appearance that the output was written "above" the prompt.

By what combination of readline redisplay functions (or otherwise) can this be achieved?

(Redisplay function documentation: http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC35)

problem demo:

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

    bool run = true;

    void* log_thread(void*)
    {
            while (run)
            {
                    sleep(1);
                    // WHAT TO DO HERE?
                    write(1, "tick\n", 5);
            }
    }

    int main()
    {
            pthread_t t;
            pthread_create(&t, 0, log_thread, 0);

            while (true)
            {
                    char* p = readline("? ");
                    free(p);

                    if (!p)
                            break;
            }

            run = false;
            pthread_join(t,0);
    }

build:

$ g++ -pthread -lreadline test.cpp
$ ./a.out

observed output: (input "foo\nbar\n" typed slowly)

? tick
ftick
otick
otick

? tick
tick
bartick
tick

? tick
^C

desired output: (input "foo\nbar\n" typed slowly)

tick
tick
tick
tick
tick
? foo

tick
tick
tick
tick
tick
? bar

tick
? ^C

回答1:


I'm doing this in the console version of my program omphalos (https://github.com/dankamongmen/omphalos). This particular code comes from https://github.com/dankamongmen/omphalos/blob/master/src/ui/tty/tty.c.

I have:

// Call whenever we generate output, so that the prompt is updated
static inline void
wake_input_thread(void){
    if(input_tid){
            pthread_kill(*input_tid,SIGWINCH);
            rl_redisplay(); // FIXME probably need call from readline contex
    }
    pthread_mutex_unlock(&promptlock);

}

and

static inline void
clear_for_output(FILE *fp){
    fputc('\r',fp);
}

Whenever something wants to print, it takes the lock and calls clear_for_output(), moving the cursor to the beginning of the current line. It can change the prompt at this time if necessary, by calling rl_set_prompt(). When done, it calls wake_input_thread(), releasing the lock and causing a redisplay.

I'm not sure if this works in the case where you've typed more than a line of text in, and doubt it, and don't care to formally discover what's likely a new and depressing bug right this moment, so you can experiment with that yourself.




回答2:


The functions that should be used:

  • rl_clear_visible_line(). Printing \r won't do it well, because it just moves the cursor to the start of the line without deleting the line content, plus it fails to work properly when there's more than one input line.
  • rl_on_new_line(); rl_redisplay(); (or rl_forced_update_display();): After printing.

It appears that it's okay to call these two functions from any thread; however it may introduce race conditions (the documentation says nothing be whether it's safe to use readline functions from multiple threads), therefore it's better to use rl_event_hook and rl_getc_function (because rl_event_hook is not called when a key is held) to call the function for the main thread. Also remember to handle the says when there's no running readline function.



来源:https://stackoverflow.com/questions/9300974/gnu-readline-libreadline-displaying-output-message-asynchronously

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!