[英]Is there a nice way of handling multi-line input with GNU readline?
[英]GNU Readline: how to clear the input line?
我通過注冊回調函數,以“選擇”的方式使用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 <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();
}
您可以做的一件事是使用\\r
跳轉到服務器輸出的行的開頭。 然后,您可以使用字段寬度說明符將輸出右鍵填充到行的其余部分。 實際上,這將覆蓋用戶已經輸入的內容。
fprintf (stdout, "\r%-20s\n", "SERVER OUTPUT");
在執行此操作之前,您可能需要fflush(stdout)
以確保緩沖區處於一致狀態。
我試圖用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();
}
這些功能有幫助嗎?
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();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.