2016-10-29 15 views
1

私がやっていることは、テキストが継続的に更新されるチャットウィンドウと、ユーザがメッセージを入力して追加できる入力ウィンドウですチャットにノンブロッキングの方法でncursesの `getstr`から完全な文字列を取得する

getstr()getstr()のような方法で看護師と使用することが可能かどうかは、私がエンターキーを押すと文字列を返すことになりますか?

これにより、新しいメッセージのTCP接続を継続的に監視し、ユーザーが完全なメッセージを入力するとメッセージをチャットに追加することができます。

私はのユーザーを示唆している投稿を見ましたが、これによりユーザーが一定期間非アクティブになったときにユーザーが入力した内容が返されます。これは実際に私がこの場合のように探しているのではなく、ユーザーが入力している間にまだブロックされているのではなく、ユーザーがメッセージの途中で何を書いているのか考えるのをやめて、ユーザーの前にテキストがgetstr()これがEnterキーを押して送信したいというメッセージであることを確認しました。

getstr()を動作させることについて特に興味があります。手動で削除/バックスペース/カーソル移動操作を手動で処理する必要はありません。私のプロジェクトの範囲を考えると、少し大きすぎる投資です。

+0

ユーザーが入力を入力している間に画面の他の部分をレンダリングしたい場合は、カーソルを移動する必要があります。これにより常にレンダリングが発生します(レンダラーがまだ文字を送信している間にユーザーが入力した場合)。そのため、あなたは左/右/削除/バックスペースの独自の処理を実装することにかなり悩まされています。しかし、何かをレンダリングする必要がなければ、2番目のスレッドを使うことができます。 – Dave

+0

私は2番目のスレッドを使用して入力を取得し、必要に応じて入力をメインプログラムに渡すことができますか?これにより、メインプログラムは受信メッセージのレンダリングを続けることができますか? –

+0

いいえ、ncursesを複数のスレッドから使​​用することはできません(なぜなら、入力を待っている間にレンダリングする必要がなければ、スレッドはオプションにすぎません)。 ncursesはstdinとstdoutを使って端末と話すので、レンダリングと読み取りの両方に使用する必要のある単一のカーソルに限定されていることに注意してください。 – Dave

答えて

1

私も同様の要件(チャットアプリケーションではありません)のものに取り組んでいます。

私が前に言ったことを繰り返すには、何かをレンダリングする必要がなければ、もう1つのスレッドを使うことができます。あなたがの場合、は入力を待つ間にレンダリングする必要があります。入力を自分で処理する必要があります。

なぜですか? ncursesは端末に向かってstdin/stdoutまで通話しているだけなのでつまり、入力と出力の両方を処理するカーソルが1つしかないことを意味します。そのため、カーソルを移動して出力を印刷すると、進行中の入力が乱れることになります。

しかし、それは解釈するのが難しくありません&入力を自分でレンダリングします。ここに私の最初のパスソリューションの縮小版です:

// Compile with -lncurses 

#include <ncurses.h> 
#include <string.h> 
#include <ctype.h> 
#include <stdlib.h> 

struct input_line { 
    char *ln; 
    int length; 
    int capacity; 
    int cursor; 
    int last_rendered; 
}; 

void make_buffer(struct input_line *buf) { 
    buf->ln = NULL; 
    buf->length = 0; 
    buf->capacity = 0; 
    buf->cursor = 0; 
    buf->last_rendered = 0; 
} 

void destroy_buffer(struct input_line *buf) { 
    free(buf->ln); 
    make_buffer(buf); 
} 

void render_line(struct input_line *buf) { 
    int i = 0; 
    for(; i < buf->length; i ++) { 
     chtype c = buf->ln[i]; 
     if(i == buf->cursor) { 
      c |= A_REVERSE; 
     } 
     addch(c); 
    } 
    if(buf->cursor == buf->length) { 
     addch(' ' | A_REVERSE); 
     i ++; 
    } 
    int rendered = i; 
    // Erase previously rendered characters 
    for(; i < buf->last_rendered; i ++) { 
     addch(' '); 
    } 
    buf->last_rendered = rendered; 
} 

int retrieve_content(struct input_line *buf, char *target, int max_len) { 
    int len = buf->length < (max_len - 1) ? buf->length : (max_len - 1); 
    memcpy(target, buf->ln, len); 
    target[len] = '\0'; 
    buf->cursor = 0; 
    buf->length = 0; 
    return len + 1; 
} 

void add_char(struct input_line *buf, char ch) { 
    // Ensure enough space for new character 
    if(buf->length == buf->capacity) { 
     int ncap = buf->capacity + 128; 
     char *nln = (char*) realloc(buf->ln, ncap); 
     if(!nln) { 
      // Out of memory! 
      return; 
     } 
     buf->ln = nln; 
     buf->capacity = ncap; 
    } 

    // Add new character 
    memmove(
     &buf->ln[buf->cursor+1], 
     &buf->ln[buf->cursor], 
     buf->length - buf->cursor 
    ); 
    buf->ln[buf->cursor] = ch; 
    ++ buf->cursor; 
    ++ buf->length; 
} 

int handle_input(struct input_line *buf, char *target, int max_len, int key) { 
    if(!(key & KEY_CODE_YES) && isprint(key)) { 
     add_char(buf, key); 
     return 0; 
    } 

    switch(key) { 
    case ERR: /* no key pressed */ break; 
    case KEY_LEFT: if(buf->cursor > 0)   { buf->cursor --; } break; 
    case KEY_RIGHT: if(buf->cursor < buf->length) { buf->cursor ++; } break; 
    case KEY_HOME: buf->cursor = 0;   break; 
    case KEY_END: buf->cursor = buf->length; break; 
    case '\t': 
     add_char(buf, '\t'); 
     break; 
    case KEY_BACKSPACE: 
    case 127: 
    case 8: 
     if(buf->cursor <= 0) { 
      break; 
     } 
     buf->cursor --; 
     // Fall-through 
    case KEY_DC: 
     if(buf->cursor < buf->length) { 
      memmove(
       &buf->ln[buf->cursor], 
       &buf->ln[buf->cursor+1], 
       buf->length - buf->cursor - 1 
      ); 
      buf->length --; 
     } 
     break; 
    case KEY_ENTER: 
    case '\r': 
    case '\n': 
     return retrieve_content(buf, target, max_len); 
    } 
    return 0; 
} 

int get_line_non_blocking(struct input_line *buf, char *target, int max_len) { 
    while(1) { 
     int key = getch(); 
     if(key == ERR) { 
      // No more input 
      return 0; 
     } 
     int n = handle_input(buf, target, max_len, key); 
     if(n) { 
      return n; 
     } 
    } 
} 

int main(void) { 
    initscr(); 

    cbreak();    // Immediate key input 
    nonl();    // Get return key 
    timeout(0);   // Non-blocking input 
    keypad(stdscr, 1); // Fix keypad 
    noecho();    // No automatic printing 
    curs_set(0);   // Hide real cursor 
    intrflush(stdscr, 0); // Avoid potential graphical issues 
    leaveok(stdscr, 1); // Don't care where cursor is left 

    struct input_line lnbuffer; 
    make_buffer(&lnbuffer); 

    int lines_read = 0; 
    while(1) { 
     char ln[1024]; 
     int len = get_line_non_blocking(&lnbuffer, ln, sizeof(ln)); 
     if(len > 0) { 
      if(strcmp(ln, "exit") == 0) { 
       break; 
      } 
      mvaddstr(7 + lines_read, 5, ln); 
      lines_read ++; 
     } 
     move(5, 5); 
     render_line(&lnbuffer); 

     // Show that we are active 
     mvaddch(2, 2, '0' + (rand() % 10)); 
     // (probably a good idea to sleep here) 
    } 

    destroy_buffer(&lnbuffer); 
    delwin(stdscr); 
    endwin(); 
    refresh(); 

    return 0; 
} 

そこに実装されていない制御文字(特にINSERT)の多くがありますが、あなたが重要だと思うものを追加するのは非常に簡単ですあなたの特定のアプリケーションに。また、ユニコード(推奨)が必要な場合は、ncurseswとその代替機能を使用する必要があることにも注意してください。

+0

これはすごくうまくいった。私はユニコードで動作させるために少し変更する必要がありましたが、それほど大きな問題はありませんでした。どうもありがとうございました! –

関連する問題