7

次のようなコールバック関数を登録することにより、GNUReadlineを「選択」方式で使用します。

rl_callback_handler_install("", on_readline_input);

そして、のループrl_callback_read_charのコールバックとしてフックアップします。それはすべてかなり標準的なものであり、正常に機能します。select()STDIN_FILENO

現在、私のプログラムはメッセージを非同期で画面に出力し、ユーザーからの入力とインターリーブされることもあります。「クリーンな」セッションは次のようになります。

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

ユーザーには3つのコマンドを入力したように見えるため、これは悪いことです(3つの入力に対する3つの応答は、実際に起こったことである2つの入力に対する3つの応答と同じくらい可能です)。

厄介なハック(これは機能します)はこれを行うことです:

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機能を使用しますか?

4

6 に答える 6

6

かなりのハッキングの後、私はこのメカニズムを手に入れることができました。他の人にも役立つといいですね。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();
    }
于 2010-06-08T21:31:10.747 に答える
3

スペースあり?"\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.
于 2009-10-02T22:14:09.447 に答える
1

実行できることの1つは\r、サーバー出力の行の先頭にジャンプするために使用することです。次に、フィールド幅指定子を使用して、出力を行の残りの部分に右パディングできます。これにより、事実上、ユーザーがすでに入力したものはすべて上書きされます。

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

fflush(stdout)その前に、バッファが一貫した状態にあることを確認することをお勧めします。

于 2009-10-03T13:41:23.260 に答える
0

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();
}
于 2009-10-03T13:18:54.713 に答える
0

これらの機能のいずれかが役立ちますか?

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

また、サーバー出力を仲介できますか?サーバー出力を制御して、ユーザーが入力している内容を単に広めるのではなく、必要なときに必要な場所にのみ表示されるようにしますか?たとえば、アプリケーションがcursesモードで実行されている場合、ユーザー入力用に予約された1つのサブウィンドウと残りの出力(サーバー出力と受け入れられたユーザー入力)の下部に1行か2行の分割ウィンドウを作成できますか?その上の2番目のサブウィンドウ?

于 2009-10-03T14:45:25.627 に答える
0

これもうまくいくようです:

rl_clear_visible_line();
printf(...);
rl_reset_line_state();
rl_redisplay();
于 2016-12-15T22:32:15.777 に答える