Reputation: 68588
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
Upvotes: 1
Views: 1874
Reputation: 366
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.
Upvotes: 1
Reputation: 3935
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.
Upvotes: 0